Dart 省道舍入误差
这样做Dart 省道舍入误差,dart,Dart,这样做打印(0.3-0.2)将打印0.09999999998 我知道浮点运算对于二进制处理器来说是不可能正确的,但我希望Dart内置的东西至少能消除舍入误差 让上面的内容显示0.1需要进行一些来回转换,我不希望这样做: print(num.parse((0.3 - 0.2).toStringAsPrecision(8))); -> 0.1 我有什么办法不疯狂地使用小数?Dart中有没有内置的东西可以帮助您实现这一点?似乎只有一个库可以实现上述功能:?大多数人做的是乘法、算术、取整,然后
打印(0.3-0.2)代码>将打印0.09999999998
我知道浮点运算对于二进制处理器来说是不可能正确的,但我希望Dart内置的东西至少能消除舍入误差
让上面的内容显示0.1需要进行一些来回转换,我不希望这样做:
print(num.parse((0.3 - 0.2).toStringAsPrecision(8)));
-> 0.1
我有什么办法不疯狂地使用小数?Dart中有没有内置的东西可以帮助您实现这一点?似乎只有一个库可以实现上述功能:?大多数人做的是乘法、算术、取整,然后除以所需的精度
final precision = 10;
final difference = (0.3 * precision) - (0.2 * precision);
final result = difference.round() / precision; // will give you 0.1
.round()
确保在返回到所需精度之前,数学运算后的任何尾随小数都会被舍入。您可以通过以下方式将值舍入到十的倍数(或任何其他数字):
然后可以对初始值或最终结果执行此操作:
int precision = 10;
final result = roundTo(0.3 - 0.2, precision);
// or inlined:
final result = ((0.3 - 0.2) * precision).round() / precision;
这确保了计算是在原始值上完成的,并且只对最终结果进行四舍五入
如果您知道您的输入值都具有相同的比例,那么可以按照@brian gorman的建议,首先对值进行比例调整,然后在最后对结果进行四舍五入和向下比例调整。为此,我建议尽早对输入值进行四舍五入,这样计算就不会累积不精确性。
(这对于一次减法并不重要,但对于一次更复杂的计算,可能会有影响)
请注意:您看到的结果不是舍入错误,而是IEEE-754定义的64位浮点数的正确结果
您将在使用普通双精度的任何其他语言(包括JavaScript和Java)中看到相同的结果。0.3-0.2
的结果不是表示为0.1
的双精度。它是一个不同的数字,因此它必须具有不同的toString
表示形式
0.1、0.2或0.3都不能精确表示为双精度。实际值为:
0.1
:0.10000000000000055115123125782702118158340451015625
0.2
:0.2000000000011102230246256540423631668090908203125
0.3
:0.29999999999988897769753748434595763683319091796875
0.3-0.2
:0.099999999997779553950749686919152736663818359375
因此,当您编写语法0.3
和0.2
时,实际上是在指定上面的精确双精度值
结果就是这样,因为0.3-0.2的精确数学计算是
0.299999999999999988897769753748434595763683319091796875
- 0.200000000000000011102230246251565404236316680908203125
= 0.099999999999999977795539507496869191527366638183593750
结果是一个精确的双倍值。所以结果是0.09999。。。正是“0.3”和“0.2”之间的差值,因此是正确的减法结果。(错误是假设您实际上有0.3和0.2作为值,但您从未这样做)。它与0.1表示的数字也不相同。感谢您提供了详尽的答案。你能澄清一下你所说的0.0999999
是什么意思吗?我很欣赏提前四舍五入的建议。在以前的一个项目中,我们努力消除潜入的、难以消除的舍入错误。Tbh。我仍然把它们称为舍入误差,而不是不精确性,因为当一个人写0.3-0.2时,潜在的假设是写的值是正在计算的值,而不是潜在的IEEE-754表示。不过,为了做出更好的决策,了解引擎盖下发生的事情仍然很有价值。无论如何,非常感谢你的回答!是否存在舍入误差可能是一个哲学上的讨论。可以说,语言绕过代码> 0.2 /代码>到最近的双值,并且可以认为是一个错误。或者你也可以定义语法0.2
表示这个双值,并且假设它是数学值2/10是一个(人类的假设)错误。波塔托,波塔托,结果是一样的,我们这些可怜的人只是在这里指导计算机做什么。从定义上讲,计算机总是正确的,只是指令可能用于错误的东西。:)旁白:永远不要用浮点计算货币。记住《超人4》和《办公空间》:)
final difference = (0.3 * precision).round() - (0.2 * precision).round();
final result = difference / precision;
0.299999999999999988897769753748434595763683319091796875
- 0.200000000000000011102230246251565404236316680908203125
= 0.099999999999999977795539507496869191527366638183593750