Ось що відбувається у двійковому. Як ми знаємо, деякі значення з плаваючою комою не можуть бути представлені точно у двійковій формі, навіть якщо вони можуть бути представлені точно у десятковій формі. Ці 3 числа - лише приклади цього факту.
За допомогою цієї програми я виводить шістнадцяткові подання кожного числа та результати кожного додавання.
public class Main{
public static void main(String args[]) {
double x = 23.53; // Inexact representation
double y = 5.88; // Inexact representation
double z = 17.64; // Inexact representation
double s = 47.05; // What math tells us the sum should be; still inexact
printValueAndInHex(x);
printValueAndInHex(y);
printValueAndInHex(z);
printValueAndInHex(s);
System.out.println("--------");
double t1 = x + y;
printValueAndInHex(t1);
t1 = t1 + z;
printValueAndInHex(t1);
System.out.println("--------");
double t2 = x + z;
printValueAndInHex(t2);
t2 = t2 + y;
printValueAndInHex(t2);
}
private static void printValueAndInHex(double d)
{
System.out.println(Long.toHexString(Double.doubleToLongBits(d)) + ": " + d);
}
}
printValueAndInHex
Метод просто помічник шестигранного принтера.
Вихід такий:
403787ae147ae148: 23.53
4017851eb851eb85: 5.88
4031a3d70a3d70a4: 17.64
4047866666666666: 47.05
--------
403d68f5c28f5c29: 29.41
4047866666666666: 47.05
--------
404495c28f5c28f6: 41.17
4047866666666667: 47.050000000000004
Перші 4 цифри x
, y
, z
, і s
«и шістнадцятиричні уявлення. У поданні IEEE з плаваючою точкою біти 2-12 представляють двійковий показник , тобто масштаб числа. (Перший біт - біт знака, а решта бітів для мантіси .) Представлений показник - це фактично двійкове число мінус 1023.
Експоненти для перших 4 чисел витягуються:
sign|exponent
403 => 0|100 0000 0011| => 1027 - 1023 = 4
401 => 0|100 0000 0001| => 1025 - 1023 = 2
403 => 0|100 0000 0011| => 1027 - 1023 = 4
404 => 0|100 0000 0100| => 1028 - 1023 = 5
Перший набір доповнень
Друге число ( y
) має меншу величину. Додаючи ці два числа для отримання x + y
, останні 2 біти другого числа ( 01
) зміщуються поза діапазоном і не враховуються в обчисленні.
Друге додавання додає x + y
і z
додає два числа однакової шкали.
Другий набір доповнень
Тут, x + z
відбувається спочатку. Вони однакової шкали, але дають число, яке вище за шкалою:
404 => 0|100 0000 0100| => 1028 - 1023 = 5
Друге додавання додає x + z
і y
, і тепер для додавання чисел ( ) випадають 3 біти . Тут має бути кругле вгору, тому що результат - наступне число з плаваючою комою вгору: для першого набору доповнень проти другого набору доповнень. Ця помилка є достатньо істотною для відображення у роздруківці загальної кількості.y
101
4047866666666666
4047866666666667
На закінчення будьте обережні, виконуючи математичні операції над номерами IEEE. Деякі уявлення неточні, і вони стають ще більш неточними, коли масштаби різні. Додайте і віднімайте числа аналогічного масштабу, якщо зможете.
(2.0^53 + 1) - 1 == 2.0^53 - 1 != 2^53 == 2^53 + (1 - 1)
). Отже, так: будьте обережні, обираючи порядок сум та інших операцій. Деякі мови надають вбудований для виконання "високоточних" сум (наприклад, pythonmath.fsum
), тому ви можете розглянути можливість використання цих функцій замість алгоритму наївної суми.