Недостающая копейка
Вы складываете строки, бухгалтерия клиента складывает строки, и два итога различаются на копейку. Никто не ошибся в арифметике. Причин две — округление в разный момент и то, как компьютеры хранят десятичные дроби — и обе устранимы.
Деньги — не десятичное число, а целое число копеек
Компьютеры хранят 0,1 и 0,2 как двоичные дроби, очень близкие, но неточные, поэтому 0,1 плюс 0,2 в большинстве языков не равно 0,3. На одной строке этого никто не замечает. На сорока строках и расчёте налога ошибка накапливается в видимую копейку. Приложение никогда не держит сумму как десятичную: каждая цена в момент ввода превращается в целое число **копеек**, вся арифметика идёт на целых, а обратное преобразование нужно только для показа.
Округлять один раз, в одном месте
Вторая причина тоньше. Возьмите три строки по 3 штуки по 0,335. Округлив сначала каждую строку, получите 1,01 трижды — итого 3,03. Сложив сначала и округлив один раз, получите 3,02. Арифметически неверного нет; это ответы на разные вопросы. Неверно делать и то и другое в одном документе — показывать округлённые строки, а суммировать неокруглённые значения, — потому что получается счёт, в котором видимые строки не дают видимого итога. Приложение округляет один раз, по строке, и складывает округлённые строки.
Где скидка прячет копейку
Процентная скидка от итога не делится поровну по строкам. Десять процентов от трёх строк по 3,33 — это 0,999, треть копейки на строку, что напечатать нельзя. Округлив долю каждой строки вниз, вы теряете копейку из итога; вверх — приобретаете лишнюю. Приложение считает скидку от промежуточного итога, распределяет её по строкам и кладёт остаток на одну из них, чтобы сумма строк со скидкой в точности равнялась итогу со скидкой.
Валюты без копеек
Не у каждой валюты два знака. У японской иены их нет вовсе: сумма — целое число иен, и округлять не до чего. Система с жёстко зашитыми двумя знаками выдаёт цены вроде ¥1 250,00 — иену так не пишут — и, что хуже, округляет до сотой доли несуществующей единицы. Приложение хранит число знаков **для каждой валюты**.
Частые вопросы
Почему счёт расходится с таблицей на копейку?
Обычно потому, что один округляет каждую строку, а другой только итог. Оба подхода защитимы; смешивать их в одном документе нельзя.
Округлять по строкам или по итогу?
По строкам, а затем складывать округлённые строки — тогда напечатанные строки видимо дают напечатанный итог.
0,1 + 0,2 действительно не 0,3?
В двоичной плавающей запятой — нет, это 0,30000000000000004. Поэтому деньги ведут в целых копейках.
Почему процентная скидка оставляет остаток?
Потому что процент от итога редко делится на целые копейки по строкам. Остаток должен лечь на одну строку.
Как приложение обходится с иеной?
Как с валютой без знаков после запятой: целые иены, без разменной единицы и без округления до копеек.
Нельзя ли просто поправить итог вручную?
Нет. Итог, не равный сумме строк, — первое, что замечает проверяющий. Исправьте правило округления.
Счета, в которых итог действительно сходится. Invoice Maker собирает счета, проформы, коммерческие предложения и расписки прямо в браузере, считает деньги целыми копейками, чтобы строка налога не уползала, и выдаёт PDF. Ничего не загружается на сервер.
Сделать счёт в браузере