Java 和浮点运算

Java and floating point arithmetic

有代码

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

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

猜测

尝试"double"键。如果还不够,试试"long double".

虽然值0.00000001ffloat的精度范围内,但值-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):

           sign
           | exponent       significand
           - -------- -----------------------
epsilon  = 0 01100100 01010111100110001110111
epsilon2 = 0 01100101 01010111100110001110111
n        = 1 10000110 01101000000000000000000
result   = 1 10000110 01101000000000000000000

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

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

按照之前答案中的建议,最好的解决方案是使用 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]