飞镖舍入误差

Dar*_*ron 4 dart

所以做一个print(0.3 - 0.2);会打印0.09999999999999998

我知道浮点运算对于二进制处理器来说是不可能正确的,但我希望 Dart 中内置的一些东西至少会尝试消除舍入错误。

使上面显示 0.1 需要来回进行一些转换,我宁愿不这样做:

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

-> 0.1
Run Code Online (Sandbox Code Playgroud)

我有什么选择可以不让小数变得疯狂?Dart 中是否有任何内置功能可以帮助解决此问题?似乎只有一个库可以执行上述操作:https : //pub.dev/packages/decimal

lrn*_*lrn 5

您可以通过以下方式将值四舍五入为十的倍数(或任何其他数字):

double roundTo(double value, double precision) => (value * precision).round() / precision;
Run Code Online (Sandbox Code Playgroud)

然后,您可以对初始值或最终结果执行此操作:

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

这可确保对原始值进行计算,并且您只对最终结果进行四舍五入。

如果您知道您的输入值都具有相同的比例,您可以按照@brian-gorman 的建议进行操作并首先缩放值,然后最后舍入和缩小结果。对于这种用途,我建议尽早对传入的值进行舍入,以便计算不会累积不精确。(这对于单个减法无关紧要,但对于更复杂的计算,它可能)。

final difference = (0.3 * precision).round() - (0.2 * precision).round();
final result = difference / precision;
Run Code Online (Sandbox Code Playgroud)

记录:您看到的结果不是舍入错误,而是正确的结果 - 对于 IEEE-754 定义的 64 位浮点数。

您将在使用普通双精度的任何其他语言(包括 JavaScript 和 Java)中看到相同的结果。的结果0.3 - 0.2 不是表示为 的双精度值0.1。它是一个不同的数字,因此它必须具有不同的toString表示形式。

0.1、0.2 或 0.3 都不能准确地表示为双打。实际值为:

  • 0.1 : 0.100000000000000055511151231257827021181583404541015625
  • 0.2 : 0.200000000000000011102230246251565404236316680908203125
  • 0.3 : 0.29999999999999988897769753748434595763683319091796875
  • 0.3 - 0.2 : 0.09999999999999997779553950749686919152736663818359375

因此,当您编写语法 0.3and 时0.2,您实际上是在指定上面的精确双精度值。

结果变成这样,因为 0.3 - 0.2 的精确数学计算是

  0.299999999999999988897769753748434595763683319091796875
- 0.200000000000000011102230246251565404236316680908203125 
= 0.099999999999999977795539507496869191527366638183593750
Run Code Online (Sandbox Code Playgroud)

结果是一个精确的双精度值。所以结果 0.09999... 正是“0.3”和“0.2”之间的差,因此是减法的正确结果。(错误是假设您实际上将 0.3 和 0.2 作为值,而您从未这样做过)。它也没有为数字表示由0.1是相同的。