(Примітка. Я додаю "b", щоб вказати двійкові числа тут. Усі інші числа вказані у десятковій кількості)
Один із способів думати про речі - це через щось подібне до наукових позначень. Ми звикли бачити числа, виражені в наукових позначеннях, наприклад, 6.022141 * 10 ^ 23. Числа з плаваючою комою зберігаються всередині, використовуючи аналогічний формат - мантісу та експонент, але використовуючи потужність двох замість десяти.
Ваш 61.0 може бути переписаний як 1.90625 * 2 ^ 5, або 1.11101b * 2 ^ 101b з мантіссою та експонентами. Щоб помножити це на десять і (перемістити десяткову точку), ми можемо зробити:
(1.90625 * 2 ^ 5) * (1.25 * 2 ^ 3) = (2.3828125 * 2 ^ 8) = (1.19140625 * 2 ^ 9)
або в мантії та експонентах у двійковій формі:
(1.11101b * 2 ^ 101b) * (1.01b * 2 ^ 11b) = (10.0110001b * 2 ^ 1000b) = (1.00110001b * 2 ^ 1001b)
Зверніть увагу, що ми там зробили для множення чисел. Ми помножили мантіси і додали показники. Потім, оскільки мантіса закінчилася більше двох, ми нормалізували результат, натискаючи на показник. Це так само, як коли ми регулюємо показник після операції над числами в десяткових наукових позначеннях. У кожному випадку значення, з якими ми працювали, мали кінцеве подання у двійковій формі, і тому значення, що виводяться за допомогою операцій основного множення та додавання, також давали значення з кінцевим поданням.
А тепер подумайте, як ми ділимо 61 на 10. Почнемо з ділення мантіс, 1.90625 та 1.25. У десятковій формі це дає 1,525, хороше коротке число. Але що це, якщо ми перетворимо його на бінарне? Ми зробимо це звичайним способом - віднімаємо найбільшу потужність двох, коли це можливо, як і перетворюємо цілі числа десяткових знаків у двійкові, але ми будемо використовувати негативні сили двох:
1,525 - 1 * 2 ^ 0 -> 1
0,525 - 1 * 2 ^ -1 -> 1
0,025 - 0 * 2 ^ -2 -> 0
0,025 - 0 * 2 ^ -3 -> 0
0,025 - 0 * 2 ^ -4 -> 0
0,025 - 0 * 2 ^ -5 -> 0
0,025 - 1 * 2 ^ -6 -> 1
0,009375 - 1 * 2 ^ -7 -> 1
0,0015625 - 0 * 2 ^ -8 -> 0
0,0015625 - 0 * 2 ^ -9 -> 0
0,0015625 - 1 * 2 ^ -10 -> 1
0.0005859375 - 1 * 2 ^ -11 -> 1
0,00009765625 ...
Ой-ой. Тепер ми в біді. Виявляється, що 1.90625 / 1.25 = 1.525 є повторюваною дробою, вираженою у двійковій формі: 1.11101b / 1.01b = 1.10000110011 ... b Наші машини мають стільки бітів, щоб утримувати цю мантісу, і тому вони просто округлить дріб і припустимо нулі понад певну точку. Помилка, яку ви бачите при поділі 61 на 10, є різницею між:
1.100001100110011001100110011001100110011 ... b * 2 ^ 10b
і, скажімо:
1.100001100110011001100110b * 2 ^ 10b
Саме це округлення мантіси призводить до втрати точності, яку ми асоціюємо зі значеннями з плаваючою точкою. Навіть коли мантісу можна точно виразити (наприклад, при додаванні двох чисел), ми все одно можемо отримати числові втрати, якщо мантіса потребує занадто багато цифр, щоб підходити після нормалізації показника.
Насправді ми робимо подібні речі весь час, коли округляємо десяткові числа до керованого розміру і просто даємо перші кілька цифр. Оскільки ми виражаємо результат у десятковій частині, це здається природним. Але якби ми округлили десятковий і потім перетворили його на іншу основу, воно виглядатиме так само потворно, як і десяткові числа, які ми отримуємо завдяки округленню з плаваючою комою.