【问题标题】:Java double precision with constant multiplication/division [duplicate]具有常数乘法/除法的Java双精度[重复]
【发布时间】:2012-07-16 11:41:04
【问题描述】:

可能重复:
Retain precision with Doubles in java

你知道Java中这两种操作的区别吗?

final double m1 = 11d / 1e9; // gives 1.1e-8
final double m2 = 11d * 1e-9; // gives 1.1000000000000001e-8

我在生成的字节码中看到 m2 的预编译结果已经不是我所期望的了。
javap -verbose -c 的输出中,我可以看到以下值:

const #3 = double   1.1E-8d;
[...]
const #6 = double   1.1000000000000001E-8d;

当我在其他表达式中使用 m1 或 m2 时,我没有相同的结果。

当我在 C 中尝试同样的事情时,m1 和 m2 严格来说是 1.1e-8

我认为我的问题在于 java 处理双精度计算的方式,但我无法解释自己错过了什么。

【问题讨论】:

    标签: java double bytecode


    【解决方案1】:

    Java 和 C 没有区别,都尊重 IEEE 浮点标准。唯一的区别可能是您打印它的方式。确切的数字 1.1e-8 不能表示为 double。输出的差异可能归因于表示的误差是如何累积的细节,以及使用什么舍入来打印结果。

    【讨论】:

    • 我认为区别仅在于累积了多少错误,第二行刚刚超过打印的阈值。
    【解决方案2】:

    您的区别在于 1e9 是完全可表示的,而 1e-9 是不完全可表示的。一个小的表示错误会导致您可以看到的算术错误。

    System.out.println(new BigDecimal(1e9));
    System.out.println(new BigDecimal(1e-9));
    

    打印

    1000000000
    1.0000000000000000622815914577798564188970686927859787829220294952392578125E-9
    

    区别不在于您使用的是* 还是/,而是您在一种情况下使用了整数,而在另一种情况下使用了分数。

    System.out.println(11 * 1e9);
    System.out.println(11 * 1e-9);
    

    打印

    1.1E10
    1.1000000000000001E-8
    

    【讨论】:

    • 好的,这就是对 Java 的解释,但现在为什么当我在 C 中执行相同的代码时,我需要所有精度?
    • C 编译器将比javac 优化代码更多。这可能意味着它可以将/ 1e-9 转换为* 1e9,因为a)它更快并且b)更准确。 Java 的编译器做的优化很少,这留给了 JIT,这也意味着去优化和优化的代码必须做同样的事情,否则你会在程序运行时看到行为的变化。 double 到 String 的转换在 C 和 Java 中也是不同的,但它不太可能是造成差异的原因。在这两种情况下,它们都使用相同的 FPU。
    • C 编译器也可以利用您计算机的 80 位浮点寄存器/计算。这取决于平台并且仅在 x86/x64 处理器上可用。 Java 在每个平台上的行为都相同,因此即使 80 位浮点可用,也只会使用 64 位浮点。
    【解决方案3】:

    如果你使用 BigDecimal 如下:

    BigDecimal a=new BigDecimal(1e-9);
    BigDecimal b=new BigDecimal(11);
    System.out.println(a.multiply(b).toString());
    

    你会看到你的号码是

    1.10000000000000006850975060355784206078677556206457666121423244476318359375E-8
    

    JVM 将您的数字四舍五入并将其更改为:

    1.1000000000000001e-8
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-06
      • 2011-12-04
      • 1970-01-01
      • 2012-08-02
      • 1970-01-01
      相关资源
      最近更新 更多