首页 > 解决方案 > 如何避免双打的舍入错误?

问题描述

我正在使用 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。

关于如何摆脱那个负号的任何想法?

标签: javamathdoublerounding

解决方案


让我们从这个开始:

如何避免双打的舍入错误?

基本上,你不能。它们是使用浮点类型的数值计算所固有的。相信我……或者花时间阅读这篇文章:

在这种情况下,发挥作用的另一件事是三角函数是通过使用有限精度(即浮点)算术计算无限级数的有限步数来实现的。该类的 javadocMath在数学函数的准确性上留下了一些“回旋余地”。值得阅读 javadocs 以了解预期的错误范围。

最后,如果您正在计算(例如)sin π/2,您需要考虑您对π/2的表示有多准确。


所以你真正应该问的是如何处理不可避免地发生的舍入误差。

在这种情况下,您要问的是如何使它看起来像您的程序的用户,就好像没有任何舍入错误一样。有两种方法:

  • 不要管它!舍入错误会发生,所以我们不应该对用户撒谎。最好教育他们。(老实说,这是高中数学,即使是“尖头老板”也应该明白算术是不精确的。)

    例程就像printf做得很好。而本例中显示的 -0.000 实际上是一个真实的答案。这意味着计算出的答案四舍五入到小数点后 3 位,但实际上是负数。对于高中数学的人来说,这实际上并不难理解。如果你解释一下。

  • 说谎。假装。放入一些特殊情况代码以将 -0.0005 和零之间的数字显式转换为零。评论中建议的代码

      System.out.printf(Locale.ROOT, "%.3f ", Math.round(v * 1000d) / 1000d);
    

    是完成这项工作的另一种方式。但这样做的风险在于,谎言在某些情况下可能是危险的。另一方面,您可以说真正的错误问题是将数字显示到小数点后 3 位。


推荐阅读