【问题标题】:Java and floating point arithmeticJava和浮点运算
【发布时间】:2015-06-23 15:46:40
【问题描述】:

有代码

public static final float epsilon = 0.00000001f;

public static final float a [] = {
        -180.0f,
        -180.0f + epsilon * 2,
        -epsilon * 2
}

a 初始化如下:

[-180.0, -180.0, -2.0E-8]

而不是想要的

[-180.0, X, Y]

如何调整epsilon 以达到预期的效果? --


1) 我希望 float 而不是 double 与之前编写的代码保持一致
2) 我不想要-179.99999998X 的任何其他特定数字,我想要X > -180.0X 尽可能接近-180.0
3) 我希望Y 尽可能接近0,但要做到float
4) 我要-180.0 < X < Y

在我最初的帖子中,我没有具体说明我想要什么。 Patricia Shanahan 通过建议 Math.ulp

猜到了这一点

【问题讨论】:

  • 您是否尝试过查阅 java 文档以了解支持的最大精度?如果这是您的问题的全部原因,使用 double 可以让您获得更高的精度....
  • 改用double? -179.99999998 超出了float 的精度。
  • 你想要什么结果?
  • @tmyklebu 我已经指定了我需要的内容。

标签: java floating-point floating-accuracy


【解决方案1】:

正如之前的答案所建议的,最好的解决方案是使用double。但是,如果您想使用float,则需要考虑其在感兴趣区域中的可用精度。该程序将您的文字 epsilon 替换为与 180f 的最低有效位相关联的值:

import java.util.Arrays;

public class Test {
  public static final float epsilon = Math.ulp(-180f);

  public static final float a [] = {
          -180.0f,
          -180.0f + epsilon * 2,
          -epsilon * 2
  };

  public static void main(String[] args) {
    System.out.println(Arrays.toString(a));
  }

}

输出:

[-180.0, -179.99997, -3.0517578E-5]

【讨论】:

  • 是的,我现在需要使用float,而不是double。我刚刚尝试了我想到的“最好的”epsilonMath.ulp(-180f) 的提示特别有用。当我评论我的答案时,我想我在计算过程中比第一次更早地失去了精度。
  • @Antonio 使用浮点数时,要非常非常小心计算的数值稳定性。由于精度有限,要确定最终结果的任何数字是否有意义并不容易。
【解决方案2】:

虽然0.00000001f 的值在float 的精度范围内,但-180f + 0.00000001f * 2 (-179.99999998) 的值不是float 的精度只有大约 7-8 位有效数字,而 -179.99999998 至少需要 11 位。所以它的最低有效位被 the addition operation 丢弃,不精确的值最终是 -180.0f

只是为了好玩,here are those values in bits (n = -180.0f):

符号 |指数有效数 - -------- ------------ ε = 0 01100100 01010111100110001110111 ε2 = 0 01100101 01010111100110001110111 n = 1 10000110 01101000000000000000000 结果 = 1 10000110 01101000000000000000000

结果最终与原始-180.0f 逐位相同。

如果您使用double,那么goes away 会出现问题,因为您没有超过double 的~15 位精度。

【讨论】:

  • 正如我之前评论的,Java 不能从-179.99999998 中删除数字。我们更早地失去了精度。我们会得到-179.99999,这将导致巨大的精度损失,因为-180 更接近-179.99999998 而不是-179.99999
  • @Antonio:你在考虑十进制。 float 以二进制形式思考,记住它是 IEEE-754 single-precision binary floating point number。被丢弃的是最不重要的。它们不会更早被丢弃,因为-180.0f2.0E-8f 都可以保存在 23 位有效位中;当你把它们加在一起时问题就来了。您溢出了可用的有效位,并且删除了最低有效位。
  • Crowdler 是的,但我们必须在获得-179.99999998 之前放弃它们。
  • @Antonio:嗯,它基本上是“在途中”获得那个价值; JLS§15.18.2 中的血腥细节,但基本上它说 "...计算精确的数学总和..." 然后 "... 总和四舍五入到最接近的值在使用 IEEE 754 舍入到最近模式的所选值集中..."。因此,从规范的字面意思看,在某些时候,有一个-179.99999998 在某处踢来踢去。 :-) (但我打赌实现可以优化,只要它们可以证明优化是不可观察的。)
  • 特别有趣的是“计算出精确的数学总和”。根据 JLS,也许这仅适用于 Java?
【解决方案3】:

尝试“双”键。如果还不够,请尝试“long double”。

【讨论】:

  • 我用 C 逻辑回答,你说得对,但上面的代码一点也不复杂; Java 真是太棒了,我的朋友 :) 顺便感谢一下警告。 @PascalCuoq
猜你喜欢
  • 2016-08-23
  • 2014-02-08
  • 1970-01-01
  • 1970-01-01
  • 2014-01-24
  • 2012-03-19
  • 2021-09-26
  • 2020-12-17
相关资源
最近更新 更多