如何避免双精度舍入错误?
How do I avoid rounding errors with doubles?
我正在使用 Math.sin 以 3 位小数精度计算 Java 中的三角函数。但是,当我计算应该导致整数的值时,我得到 1.0000000002 而不是 1。
我试过使用
System.out.printf(Locale.ROOT, "%.3f ", v);
确实解决了 1.000000002 变成 1.000.
的问题
然而,当我计算应该导致 0 的数字时,却得到 -1.8369701987210297E-16 并使用
System.out.printf(Locale.ROOT, "%.3f ", v);
在我需要它为 0.000 时打印出 -0.000。
关于如何摆脱负号的任何想法?
取决于您需要的精度,您可以乘以 X
再除以 X
其中 X 是 X=10^y
并且 y 需要浮动定位精度。
让我们从这个开始:
How do I avoid rounding errors with doubles?
基本上,你不能。它们是使用浮点类型的数值计算所固有的。相信我……或者花时间阅读这篇文章:
在这种情况下,发挥作用的另一件事是三角函数是通过使用有限精度(即浮点)算法计算无限级数的有限步数来实现的。 Math
class 的 javadoc 在数学函数的准确性上留下了一些 "wiggle room"。值得阅读 javadoc 以了解 预期 错误范围。
最后,如果您正在计算(例如)sin π/2,您需要考虑您对 π/2[=48= 的表示的准确性]是。
所以你真正应该问的是如何处理不可避免发生的舍入误差。
在这种情况下,您要问的是如何使它看起来像您的程序的用户一样,就好像没有任何舍入错误一样。有两种方法:
别管它!出现舍入误差,所以我们不应该就此对用户撒谎。最好教育他们。 (老实说,这是高中数学,甚至"the pointy haired boss"也应该明白算术不准确。)
像 printf
这样的例程做得很好。而本例中显示的 -0.000 实际上是一个真实的答案。这意味着计算出的答案四舍五入到零到小数点后三位,但实际上是负数。对于有高中数学的人来说,这实际上并不难理解。如果你解释一下。
说谎。假装。放入一些特殊情况代码以将 -0.0005 和零之间的数字显式转换为正好为零。评论中建议的代码
System.out.printf(Locale.ROOT, "%.3f ", Math.round(v * 1000d) / 1000d);
是完成这项工作的另一种方式。但这样做的风险是,在某些情况下,谎言可能很危险。另一方面,您可以说真正的错误问题是将数字显示到小数点后 3 位。
我正在使用 Math.sin 以 3 位小数精度计算 Java 中的三角函数。但是,当我计算应该导致整数的值时,我得到 1.0000000002 而不是 1。
我试过使用
System.out.printf(Locale.ROOT, "%.3f ", v);
确实解决了 1.000000002 变成 1.000.
的问题然而,当我计算应该导致 0 的数字时,却得到 -1.8369701987210297E-16 并使用
System.out.printf(Locale.ROOT, "%.3f ", v);
在我需要它为 0.000 时打印出 -0.000。
关于如何摆脱负号的任何想法?
取决于您需要的精度,您可以乘以 X
再除以 X
其中 X 是 X=10^y
并且 y 需要浮动定位精度。
让我们从这个开始:
How do I avoid rounding errors with doubles?
基本上,你不能。它们是使用浮点类型的数值计算所固有的。相信我……或者花时间阅读这篇文章:
在这种情况下,发挥作用的另一件事是三角函数是通过使用有限精度(即浮点)算法计算无限级数的有限步数来实现的。 Math
class 的 javadoc 在数学函数的准确性上留下了一些 "wiggle room"。值得阅读 javadoc 以了解 预期 错误范围。
最后,如果您正在计算(例如)sin π/2,您需要考虑您对 π/2[=48= 的表示的准确性]是。
所以你真正应该问的是如何处理不可避免发生的舍入误差。
在这种情况下,您要问的是如何使它看起来像您的程序的用户一样,就好像没有任何舍入错误一样。有两种方法:
别管它!出现舍入误差,所以我们不应该就此对用户撒谎。最好教育他们。 (老实说,这是高中数学,甚至"the pointy haired boss"也应该明白算术不准确。)
像
printf
这样的例程做得很好。而本例中显示的 -0.000 实际上是一个真实的答案。这意味着计算出的答案四舍五入到零到小数点后三位,但实际上是负数。对于有高中数学的人来说,这实际上并不难理解。如果你解释一下。说谎。假装。放入一些特殊情况代码以将 -0.0005 和零之间的数字显式转换为正好为零。评论中建议的代码
System.out.printf(Locale.ROOT, "%.3f ", Math.round(v * 1000d) / 1000d);
是完成这项工作的另一种方式。但这样做的风险是,在某些情况下,谎言可能很危险。另一方面,您可以说真正的错误问题是将数字显示到小数点后 3 位。