Я теряю 2 силы в умножении IEEE754

Я изучаю 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 как наибольшую силу.

Итак, предполагая, что визуализатор правильный:

  • Откуда взялось 2^4?
  • Почему нет потери точности при умножении 0,2 на 100, если 0,2 было неточным с самого начала? Почему c точный результат?

1 ответ

Решение

В арифметических правилах IEEE нет ничего, что препятствовало бы тому, чтобы округление до ближайшего совпадало с точным результатом десятичного вычисления, которое вы хотели сделать.

Точное значение двойного литерала 100.0, конечно, 100.

Точное значение двойного литерала 0.2 равно 0.200000000000000011102230246251565404236316680908203125

Их продукт 20.000000000000001110223024625156540423631668090820312500

Ошибка округления при округлении до 20: 1.110223024625156540423631668090820312500E-15

Ошибка округления при округлении до 20,000000000000003552713678800500929355621337890625, наименьшее двойное значение больше 20, будет 2,442490654175344388931989669799804687500E-15

Поскольку ошибка округления больше, чем ошибка округления до 20, правильный результат округления до ближайшего двойного умножения равен 20,0. Ошибка округления 2^-50 + 2^-52Ваши "потерянные" полномочия 2.

Я использовал программу Java для выполнения вычислений из-за удобного класса BigDecimal, который может точно представлять все конечные двойные числа, и результатов некоторой арифметики на них, включая умножение. Двойная арифметика Java следует 64-битной двоичной переменной с плавающей запятой IEEE 754 в режиме округления до ближайшего, что также является обычной системой для двойных Си.

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
Другие вопросы по тегам