наприклад, з доларом ви ніколи не маєте точності менше 0,01 долара
О, справді?
вікове питання, чому не слід зберігати валюту як номер IEEE 754 з плаваючою точкою.
Будь ласка, не соромтесь зберігати дюйми в номерах IEEE 754 з плаваючою комою . Вони зберігають точно так, як ви очікували.
Будь ласка, не соромтеся зберігати будь-яку суму грошей у номерах плаваючої крапки IEEE 754, які ви можете зберігати за допомогою кліщів, які ділять лінійку на частки дюйма.
Чому? Тому що, коли ви використовуєте IEEE 754 , саме так ви зберігаєте його.
Справа в дюймах - це те, що вони розділені навпіл. Справа в більшості видів валюти полягає в тому, що вони діляться на десяті частини (деякі види не є, але залишаємося зосередженими).
Ця різниця не була б такою заплутаною, за винятком того, що для більшості мов програмування введення та виведення чисел з плаваючою комою IEEE 754 виражається у десяткових колах! Що дуже дивно, оскільки вони не зберігаються у децималах.
Через це ви ніколи не можете побачити, як біти дивують речі, коли ви попросите комп'ютер зберігати 0.1
. Дивовижність ви бачите лише тоді, коли займаєтесь математикою проти неї, і у неї є дивні помилки.
Від ефективної Java Джоша Блоха :
System.out.println(1.03 - .42);
Виробляє 0.6100000000000001
Що найбільше говорить про це, це не 1
сидячий шлях справа. Це дивні номери, які потрібно було використати, щоб отримати його. Замість того, щоб використовувати найпопулярніший приклад, 0.1
ми повинні використовувати приклад, який показує проблему і уникає округлення, яке б приховало її.
Наприклад, чому це працює?
System.out.println(.01 - .02);
Виробляє -0.01
Тому що нам пощастило.
Я ненавиджу проблеми, які важко діагностувати, бо мені іноді «пощастило».
IEEE 754 просто не може точно зберігати 0,1. Але якщо ви попросите його зберігати 0,1, а потім попросите його надрукувати, він покаже 0,1, і ви подумаєте, що все добре. Це не добре, але ви цього не бачите, оскільки це округлення, щоб повернутися до 0,1.
Деякі люди плутають хек з інших, називаючи ці розбіжності помилками округлення. Ні, це не помилки округлення. Округлення робить те, що належить, і перетворює те, що не є десятком, у десяткові, щоб воно могло друкуватись на екрані.
Але це приховує невідповідність між тим, як відображається число, і тим, як воно зберігається. Помилка не сталася, коли відбулося округлення. Це сталося, коли ви вирішили ввести номер у систему, яка не може його точно зберігати, і припустили, що він зберігається саме тоді, коли його не було.
Ніхто не сподівається, що π точно зберігатиметься в калькуляторі, і їм вдається добре працювати з ним. Тому проблема навіть не в точності. Йдеться про очікувану точність. Комп'ютери відображають одну десяту частину так 0.1
само, як і наші калькулятори, тому ми очікуємо, що вони зберігатимуть одну десяту як ідеально, як це роблять наші калькулятори. Вони ні. Що дивно, адже комп’ютери дорожчі.
Дозвольте мені показати вам невідповідність:
Зауважте, що 1/2 та 0,5 ліній ідеально. Але 0,1 просто не вирівнюється. Звичайно, ви зможете наблизитись, якщо продовжуєте ділити на 2, але ніколи не вдаритесь точно. І нам потрібно все більше і більше бітів щоразу, коли ми ділимо їх на 2. Отже, для представлення 0,1 у будь-якій системі, яка ділиться на 2, потрібно нескінченна кількість бітів. Мій жорсткий диск просто не такий великий.
Тож IEEE 754 перестає пробувати, коли у нього закінчується біт. Що приємно, бо мені потрібно місце на жорсткому диску для ... сімейних фотографій. Насправді ні. Сімейні фотографії. : P
У будь-якому випадку те, що ви вводите і те, що ви бачите, є десятковими знаками ( праворуч), але те, що ви зберігаєте, - це двозначні зображення (зліва). Іноді ці ідеально однакові. Іноді їх немає. Іноді дивиться, як вони однакові, коли їх просто немає. Ось округлення.
Зокрема, що нам потрібно знати, щоб мати змогу зберігати значення у певній валюті та роздруковувати їх?
Будь ласка, якщо ви обробляєте мої гроші на десятковій основі, не використовуйте поплавці чи подвійні.
Якщо ви впевнені, що такі речі, як десяті копійки, не будуть задіяні, тоді просто зберігайте копійки. Якщо ви цього не зробите, то зрозумійте, якою буде найменша одиниця цієї валюти, і використовуйте її. Якщо ви не можете, використовуйте щось на кшталт BigDecimal .
Моя чиста вартість, ймовірно, завжди вписується в 64-бітове ціле число, але такі речі, як BigInteger, добре працюють для проектів, більших за це. Вони просто повільніше, ніж рідні типи.
З'ясувати, як його зберігати - це лише половина проблеми. Пам'ятайте, що ви також повинні мати можливість відображати це. Хороший дизайн розділить ці дві речі. Справжня проблема використання плавців тут - це те, що дві речі збиті разом.