Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dart rounding errors

Tags:

dart

So doing a print(0.3 - 0.2); will print 0.09999999999999998.

I know floating point arithmetic is impossible for a binary processor to get correct, but I was hoping for something built into Dart that would at least try to kill off the rounding errors.

Getting the above to show 0.1 takes some conversions back and forth, which I'd rather not do:

print(num.parse((0.3 - 0.2).toStringAsPrecision(8)));

-> 0.1

What are my options for not going crazy working with decimals? Is there anything built into Dart to help with this? There seems to be only one library that does the above: https://pub.dev/packages/decimal?

like image 597
DarkNeuron Avatar asked Jan 25 '23 23:01

DarkNeuron


1 Answers

You can round a value to a multiple of ten (or of any other number) by:

double roundTo(double value, double precision) => (value * precision).round() / precision;

You can then do that to either the initial values or the final result:

int precision = 10;
final result = roundTo(0.3 - 0.2, precision);
// or inlined:
final result = ((0.3 - 0.2) * precision).round() / precision; 

This ensures that the computation is done on the original values, and you only round the final result.

If you know that your input values all have the same scale, you can do as @brian-gorman suggests and scale the values first, and then round and down-scale the result at the end. For that use, I would recommend rounding early, on the incoming values, so that the computation will not accumulate imprecision. (That doesn't matter for a single subtraction, but for a more complicated computation, it might).

final difference = (0.3 * precision).round() - (0.2 * precision).round();
final result = difference / precision;

For the record: The result you are seeing is not a rounding error, it is the correct result—for 64-bit floating point numbers as defined by IEEE-754.

You will see the same result in any other language using normal doubles, including JavaScript and Java. The result of 0.3 - 0.2 is not the double represented as 0.1. It is a different number, so it must have a different toString representation.

Neither of 0.1, 0.2 or 0.3 can be represented exactly as doubles. The actual values are:

  • 0.1 : 0.1000000000000000055511151231257827021181583404541015625
  • 0.2 : 0.200000000000000011102230246251565404236316680908203125
  • 0.3 : 0.299999999999999988897769753748434595763683319091796875
  • 0.3 - 0.2 : 0.09999999999999997779553950749686919152736663818359375

So when you write the syntax 0.3 and 0.2, you are really specifying the precise double values above.

The result becomes what it is because the precise mathematical calculation of 0.3 - 0.2 is

  0.299999999999999988897769753748434595763683319091796875
- 0.200000000000000011102230246251565404236316680908203125 
= 0.099999999999999977795539507496869191527366638183593750

and the result is an exact double value. So the result 0.09999... is precisely the difference between "0.3" and "0.2" and therefore the correct result for the subtraction. (The mistake is assuming that you actually have 0.3 and 0.2 as values, you never did). It is also not the same as the number represented by 0.1.

like image 165
lrn Avatar answered Mar 27 '23 18:03

lrn