1

I wrote a little program to show my problem:

void setup() 
{
  Serial.begin(9600);

  double K=39.85;
  double a=K*100;
  Serial.print("a= ");
  Serial.println(a);

  long val_long = 0;
  val_long = a;
  Serial.print("val_long= ");
  Serial.println(val_long);
}

void loop() {
}

I get this back from my Arduino uno:

a= 3985.00
val_long=3984

I've been pondering for a long time and can't find the mistake! Why is the long value decremented by one? Can anybody help me please?

  • upvote for uncluding minimal sketch – jsotola Jan 7 at 0:44
7

The problem is that float numbers are seldom an exact representation. So the float is stored as the closest number that is exactly representable as a float, namely
10446438 × 2−18 = 39.84999847412109375.

When you multiply this by 100, you get 16322559 × 2−12 = 3984.999755859375.

And than casting to an int or long results in 3984, not 3985.

so whenever you cast a value from a float or double to an int or long, you always should add 0.5 (so all values from [x - 0.5, x + 0.5) are rounded to x assuming x >= 0, thus:

val_long = int(a + 0.5);

Beware of negative values, see therefore https://www.cs.cmu.edu/~rbd/papers/cmj-float-to-int.html

Better is to use the round function, see comment of Edgar Bonet below.

UPDATE

See comment of Lightness Races with Monica

In case it's not wanted to round up, but you still want 39.84999847412109375 to be 39.85, than you have to add a value that has the value of the least significant bit (actually slightly less but that is impossible to define). In most cases, it's best to add half the significance you want. So if you need two decimal accuracy, add 0.005.

  • 1
    Thank you so much, you made my day! Now everything works fine. – Hardy72 Jan 6 at 23:23
  • Serial.println(a,5); might have revealed that issue already. Especially on 8bit Arduinos with double being only 32 bit wide. (Same as float) – DataFiddler Jan 7 at 0:00
  • 2
    1. I edited the answer to add the exact numbers. 2. You can use round(), from the Arduino core. It's a macro that adds ±0.5 (depending on the sign) before casting to long. – Edgar Bonet Jan 7 at 9:19
  • @EdgarBonet Thank you very much. I will also add in my answer a note to your comment. – Michel Keijzers Jan 7 at 9:26
  • 2
    @MichelKeijzers If K is 39.856 then a is 3985.6, so program's output is 3985, which is presumably deliberate. Your suggestion changes the output to 3986. You've changed the meaning of the program without mentioning it. An alternative is to use "another workaround for [these] little inaccuracies [like the one causing this bug]", such as epsilons. That being said, if K will only ever have max 2 meaningful decimal places, then this is moot. – Lightness Races with Monica Jan 7 at 16:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.