我在 IEEE754 乘法中失去了 2 的幂

I'm losing 2's powers in IEEE754 multiplication

我正在研究 IEE754,我对这些代码行有点困惑

double a = 0.2;
double b = 100.0;
double c = a * b;

我知道 0.2 不能用 2 的幂完美表示,而 100 可以,但我得到了 20 作为 c 的完美结果。

可视化构成这些值的 2 的幂(我使用的是简单的 js 可视化工具:http://bartaz.github.io/ieee754-visualization/)我看到 0.2 以

开头
2^-3 + 2^-4 + 2^-7...

和 100

2^6 + 2^5 + 2^2

现在回答我的问题:这是 20,也就是 c 的样子

2^4 + 2^2
^^^

什么? 2^4 是从哪里来的?如果我用数学方法将 0.2 的所有项乘以 100 的所有项,我将得到 2^3 作为最大幂。

因此,假设可视化工具是正确的:

IEEE 算术规则中没有任何内容可以防止舍入到最接近的值达到您想要执行的小数计算的确切结果。

双字面量 100.0 的准确值当然是 100。

双字面量0.2的准确值为0.200000000000000011102230246251565404236316680908203125

他们的乘积是 20.000000000000001110223024625156540423631668090820312500

四舍五入到 20 的舍入误差为 1.110223024625156540423631668090820312500E-15

四舍五入到 20.000000000000003552713678800500929355621337890625 的舍入误差,大于 20 的最小双精度数,将是 2.442490654175344388931989669799804687500E=13-15

由于四舍五入的误差大于四舍五入到 20 的误差,所以正确的双倍乘法舍入到最近的结果是 20.0。舍入误差为 2^-50 + 2^-52,您的 2 的 "lost" 次方。

我用了一个Java程序来做计算,因为方便的BigDecimal class可以准确地表示所有有限的双精度数,以及对它们的一些算术结果,包括乘法。 Java double 运算遵循 IEEE 754 64 位二进制浮点数舍入到最近模式,这也是 C double 的常用系统。

import java.math.BigDecimal;

public class Test {
  public static void main(String[] args) {
    double a = 0.2;
    double b = 100.0;
    double c = a * b;
    display(a);
    display(b);
    display(c);
    BigDecimal exactProduct = new BigDecimal(a).multiply(new BigDecimal(b));
    System.out.println(exactProduct);
    BigDecimal down = new BigDecimal(20.0);
    System.out.println(down);
    BigDecimal up = new BigDecimal(Math.nextUp(20.0));
    System.out.println(up);
    System.out.println("Round down error "+exactProduct.subtract(down));
    System.out.println("Round up error "+up.subtract(exactProduct));
  }
  private static void display(double in){
    System.out.println(new BigDecimal(in));
  }
}

输出:

0.200000000000000011102230246251565404236316680908203125
100
20
20.000000000000001110223024625156540423631668090820312500
20
20.000000000000003552713678800500929355621337890625
Round down error 1.110223024625156540423631668090820312500E-15
Round up error 2.442490654175344388931989669799804687500E-15