【问题标题】:int and double divided by double javaint 和 double 除以 double java
【发布时间】:2014-01-08 19:34:06
【问题描述】:

我该如何解释以下内容:

    double o = 2.3;
    int i = (int) (o * 100000.0);
    double d = i / 100000.0;
    System.out.println(d);

打印 2.29999

    double o = 2.3;
    double i = o * 100000.0;
    double d = i / 100000.0;
    System.out.println(d);

打印 2.3

当将一个 int 除以 double 时,java 不是首先将 int 转换为 double 然后进行除法 - 如果是这样,两个代码块应该有效地打印相同的值?

我似乎缺少一些 IEEE 754 浮点精度规范和/或内联的 jvm 优化

 double i = 2.3 * 100000.0;
 double d = i / 100000.0;

作为 双 d = 2.3 * 100000.0 / 100000.0; 有效地取消了分裂并使其成为无操作。

想法?

【问题讨论】:

  • 这是已知的二进制操作级别问题。这就是为什么当您比较一些 double 值时,您不得使用 ==,而是使用特殊的 Compare 方法和精度设置
  • @ElliotTereschuk:问题中没有==操作,并且“二进制操作级别”不是浮点中常用的术语。
  • @EricPostpischil 使用 == 进行简单检查显然会在 2.29999 == 2.3 上返回 false,这是非常常见的错误。此评论是建议使用舍入和Double.Compare
  • @ElliotTereschuk:问题不是问为什么比较会返回意外结果;它询问为什么两个结果不同。关于比较的评论与这个问题无关。

标签: java double ieee-754


【解决方案1】:

2.3 在 IEEE-754 64 位二进制浮点中不能完全表示。在您的第一个代码序列中发生的情况是:

  • 源文本 2.3 被转换为最接近的可表示值 2.29999999999999982236431605997495353221893310546875。
  • 将其乘以 100000 的确切数学结果也不能精确表示,因此它被四舍五入为 229999.99999999997089616954326629638671875。
  • 当它转换为 int 时,转换会截断,生成 229999。
  • 再除以 100000 轮,得到 2.29999000000000020094148567295633256435394287109375。
  • 打印上述内容时,Java 将其显示为“2.29999”。

【讨论】:

  • 为什么我用 0.1 替换 2.3 时没有这个问题(不能用双精度表示)。
  • @fo_x86:有时最接近的可表示值低于原始值,有时更高。在 2.3 的情况下,它更低。在 0.1 的情况下,它更高,为 0.1000000000000000055511151231257827021181583404541015625。然后乘以 100,000 恰好产生 10,000,由于四舍五入。将其转换为 int 会产生 10,000 个。除以 100,000 得到 0.1000000000000000055511151231257827021181583404541015625,显示为“.1”。
  • 了解 Java 对浮点值的默认显示仅显示足够的位数以将该值与任何相邻值区分开来可能会有所帮助。因此,它为 0.1000000000000000055511151231257827021181583404541015625 产生“.1”,因为将“.1”转换回浮点会产生原始值。如果打印的值稍大或稍小,Java 将使用更多的数字来区分它。因此,默认情况下,Java 在显示浮点值时会“撒谎”一点。
  • 到目前为止,我发现的最简单的方法可以精确打印出双 dSystem.out.println(new BigDecimal(d))
  • 我很惊讶没有人链接到 Goldberg 的论文“What Every Computer Scientist Should Know About Floating-Point Arithmetic”。 ece.uwaterloo.ca/~dwharder/NumericalAnalysis/02Numerics/Double/… [pdf]
【解决方案2】:

我似乎缺少一些 IEEE 754 浮点精度规范

当您使用(int) 时,它会截断小数部分,无论它与下一个整数值有多接近。它是不可逆的,因为信息会丢失,因此您不会得到相同的结果也就不足为奇了。

你可以做的是以下

double o = 2.3;
long l = Math.round(o * 100000.0);
double d = l / 100000.0;
System.out.println(d);

内联的 jvm 优化 ... 使其成为无操作

JVM 可能会优化它,但不会改变结果。如果优化改变了结果,那就是优化器中的一个错误。

【讨论】:

    【解决方案3】:
    double i = o * 100000.0;
    

    i 之后的值是229999.99999999997(你可以通过添加一个简单的打印语句自己看到它;这是由于floating-point inaccuracies)。

    int i = (int) (o * 100000.0);
    

    此后i 的值为229999(即上面的值由于int 转换而截断了小数部分)。

    因此,在行中

    double d = i / 100000.0;
    

    您在两个 sn-ps 中使用了两个数值不同的 i 值(即它们相差大约 1),因此输出不同。

    你说得对,当将 int 除以 double 时,int 首先转换为 double(这是通过字节码指令 i2d 完成的)。

    【讨论】:

      猜你喜欢
      • 2011-08-25
      • 1970-01-01
      • 2013-04-26
      • 1970-01-01
      • 2015-06-08
      • 2013-07-30
      • 2013-06-02
      • 1970-01-01
      • 2010-10-11
      相关资源
      最近更新 更多