Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dart, float for loop will cause strange result

for(int i=0; i<10; i++) {
    priceValue.add(54.99 + i*5);
    print(54.99 + i*5);
}

Result:

js_primitives.dart:30 54.99
js_primitives.dart:30 59.99
js_primitives.dart:30 64.99000000000001
js_primitives.dart:30 69.99000000000001
js_primitives.dart:30 74.99000000000001
js_primitives.dart:30 79.99000000000001
js_primitives.dart:30 84.99000000000001
js_primitives.dart:30 89.99000000000001
js_primitives.dart:30 94.99000000000001
js_primitives.dart:30 99.99000000000001

What causes the 00000000001 to appear?

like image 765
Charles Avatar asked Jun 15 '17 03:06

Charles


1 Answers

It's just how double numbers work. You would get the same result in other languages that use the same doubles as Dart (they are standardized as IEEE-754 64-bit floating point numbers).

The crux is that, e.g., 89.99 cannot be represented exactly by the precision of doubles. Doubles are binary numbers with at most 53 significant bits. In binary, 89.99 doesn't have a finite representation, just like 1/3 cannot be written finitely as a decimal number (0.3333333.... it's never enough for the precise value).

There are only a finite number of double values, spread throughout the range that doubles cover. So, when you try to convert a "real" number to a double, the computer rounds to the closest double value to the original number.

Even 54.99 isn't precise. The actual double value of 54.99 is the double closest to the mathematical number 54.99 (aka 5499/100):

54.99000000000000198951966012828052043914794921875

Then you add 35 to that, which again isn't representable as a double, and the double hardware picks the closest double to that value:

89.990000000000009094947017729282379150390625

Due to the imprecission on the original 54.99, that is actually a different double than the one that would be picked for just writing 89.99, which is:

89.9899999999999948840923025272786617279052734375

When you convert a double to a string, the algorithm for picking a representation is actually clever. It picks the shortest decimal representation where the numerical value will be rounded to the original double value.

So, since 54.99 + 35 is not the same double as 89.99, they need to have different string representations. The string 89.99 is obviously taken for the latter, so 54.99 + 35 needs more digits to distinguish itself. That's where the trailing 00000000000001 comes from.

So, in short, it's just what doubles do. The same computation will give the same result in most other languages that use the same double type.

like image 91
lrn Avatar answered Nov 15 '22 09:11

lrn