Работа с числами с плавающей запятой

Темы, не касающиеся фреймворка, но относящиеся к программированию в целом.
Ответить
Аватара пользователя
vitovt
Сообщения: 210
Зарегистрирован: 2012.03.21, 10:37
Контактная информация:

Работа с числами с плавающей запятой

Сообщение vitovt »

Столкнулся с проблемой при работе с числами с плавающей запятой. Т.е с деньгами -)

В базе все числа хранятся в полях DECIMAL(16,4)

Например мне нужно посчиать сумму транзакций, например

Код: Выделить всё

SELECT SUM(amount) AS total FROM tbl_transactions
ну вот к примеру результат 130,60

В php делаю

Код: Выделить всё

$sum = $row['total'] - 130
На выходе получаю 0,59999999

где я ошибся =)
rak
Сообщения: 2181
Зарегистрирован: 2010.11.02, 23:40
Контактная информация:

Re: Работа с числами с плавающей запятой

Сообщение rak »

судя по всему http://php.net/manual/ru/language.types.float.php
Числа с плавающей точкой имеют ограниченную точность. Хотя это зависит от операционной системы, в PHP обычно используется формат двойной точности IEEE 754, дающий максимальную относительную ошибку округления порядка 1.11e-16. Неэлементарные арифметические операции могут давать большие ошибки, и, разумеется, необходимо принимать во внимание распространение ошибок при совместном использовании нескольких операций.
Кроме того, рациональные числа, которые могут быть точно представлены в виде чисел с плавающей точкой с основанием 10, например, 0.1 или 0.7, не имеют точного внутреннего представления в качестве чисел с плавающей точкой с основанием 2, вне зависимости от размера мантиссы. Поэтому они и не могут быть преобразованы в их внутреннюю двоичную форму без небольшой потери точности. Это может привести к неожиданным результатам: например, floor((0.1+0.7)*10) скорее всего вернет 7 вместо ожидаемого 8, так как результат внутреннего представления будет чем-то вроде 7.9999999999999991118....
Так что никогда не доверяйте точности чисел с плавающей точкой до последней цифры, и не проверяйте напрямую их равенство. Если вам действительно необходима высокая точность, используйте математические функции произвольной точности и gmp-функции.
"Простое" объяснение можно найти в » руководстве по числам с плавающей точкой, которое также называется "Why don’t my numbers add up?" ("Почему мои числа не складываются?")
Аватара пользователя
vitovt
Сообщения: 210
Зарегистрирован: 2012.03.21, 10:37
Контактная информация:

Re: Работа с числами с плавающей запятой

Сообщение vitovt »

Да, верно, кажется то, что нужно. Я уже грешил на DECIMAL
Аватара пользователя
maleks
Сообщения: 1985
Зарегистрирован: 2012.12.26, 12:56

Re: Работа с числами с плавающей запятой

Сообщение maleks »

Код: Выделить всё

$x = 130.6 - 130;
print $x . '<br>';
$x = bcsub('130.6', '130', 2);
print $x;
0.59999999999999
0.60
Yii2 universal module sceleton - for basic and advanced templates
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Работа с числами с плавающей запятой

Сообщение zelenin »

когда работаем с деньгами, надо работать через специализированные расширения типа bcmath.
Аватара пользователя
vitovt
Сообщения: 210
Зарегистрирован: 2012.03.21, 10:37
Контактная информация:

Re: Работа с числами с плавающей запятой

Сообщение vitovt »

zelenin писал(а): 2017.03.10, 16:06 когда работаем с деньгами, надо работать через специализированные расширения типа bcmath.
да, спасибо, как раз почитал про них из поста выше и начал использовать. Стало считаться все хорошо, осталось решить проблему округления.

Когда я использую стандартные функции округления, результат чаще всего не в мою пользу. Например я получаю цифру 0.035796833333333

если округлять ее - на выходе будет или 0.036 или в худшем случае 0.4

Идеального варианта как я понимаю нет, а все зависит от задач бизнеса. Если это финансовое приложение - то лучше, когда округление идет в сторону того, кто деньги отдает =)
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Работа с числами с плавающей запятой

Сообщение zelenin »

vitovt писал(а): 2017.03.10, 17:02Стало считаться все хорошо, осталось решить проблему округления.

Когда я использую стандартные функции округления, результат чаще всего не в мою пользу. Например я получаю цифру 0.035796833333333

если округлять ее - на выходе будет или 0.036 или в худшем случае 0.4
что за бред? нет вашей или чужой пользы. есть исключительно правило округления, заданное бизнесом.
Аватара пользователя
vitovt
Сообщения: 210
Зарегистрирован: 2012.03.21, 10:37
Контактная информация:

Re: Работа с числами с плавающей запятой

Сообщение vitovt »

Ваша или чужая польза это и есть правило заданное бизнесом.
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Работа с числами с плавающей запятой

Сообщение ElisDN »

vitovt писал(а): 2017.03.10, 17:02 Идеального варианта как я понимаю нет...
Идеальный - использовать дроби с числителем и знаменателем. Тогда в любом случае будет 10 / 3 * 3 = 10.
Аватара пользователя
rugabarbo
Сообщения: 1063
Зарегистрирован: 2015.06.21, 16:21
Контактная информация:

Re: Работа с числами с плавающей запятой

Сообщение rugabarbo »

Правила округления во время подотчётных финансовых операций регулируются законодательством. Вот пример: https://www.nalog.ru/rn77/taxation/taxes/nds/4615493/
Всё, что не регулируется законодательством, регулируется учётной политикой конкретной компании.
Krash
Сообщения: 29
Зарегистрирован: 2016.04.19, 12:43

Re: Работа с числами с плавающей запятой

Сообщение Krash »

ElisDN писал(а): 2017.03.10, 20:58
vitovt писал(а): 2017.03.10, 17:02 Идеального варианта как я понимаю нет...
Идеальный - использовать дроби с числителем и знаменателем. Тогда в любом случае будет 10 / 3 * 3 = 10.
И хранить в базе отдельно? А что делать если мне надо будет сложить 3/5 + 2/7?
Аватара пользователя
vitovt
Сообщения: 210
Зарегистрирован: 2012.03.21, 10:37
Контактная информация:

Re: Работа с числами с плавающей запятой

Сообщение vitovt »

Не не не, отдельно хранить никак нельзя. На счет работы с деньгами приведу пример:

Представим систему афилейтов, в которой участники раз в месяц получают вознаграждение за что-то. Вознаграждение рассчитывается из суммы доходов по продукту. Например, человек привел клиента, тот потратил 12.50 рублей, вам причитается 1% от этого. Вы можете посчитать точно: 12.50 * 1.01 = 12.625

Перечислить такую суммы вы не можете, потому, что нужно округлить до копеек. Значит вам надо перечислить 12 рублей 62 копейки или 63 копейки. Вроде бы по математической логике 63 копейки вернее, но что, если таких платежей у вас миллион, получается вы будете как бизнес терять на каждой операции. С другой стороны если округлить в меньшую сторону - вы будете недоплачивать клиенту, получая некоторую выгоду. С вашей стороны - решение ок, со стороны партнера - не очень.

Это пример.
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Работа с числами с плавающей запятой

Сообщение ElisDN »

Krash писал(а): 2017.03.11, 12:50 И хранить в базе отдельно?
Да.
Krash писал(а): 2017.03.11, 12:50 А что делать если мне надо будет сложить 3/5 + 2/7?
Получите 31/35.
Ответить