PHP иногда неправильно считает одни и те же числа?

12
FFFFx029A
На сайте с 01.09.2007
Offline
142
2022

Всем доброго времени суток. Сегодня столкнулся со странной проблемой.

Очень подробно:

1. Достаю из базы 3 числа из одной и той же записи в таблице

нужно_забрать_яблок 5.4

яблок_на_складе 3.25

яблок_в_наличии 2.15

2. Далее вычисляю сколько и где нужно забрать яблок, так как на складе яблок меньше чем нужно, то "забираю_со_склада"="яблок_на_складе" (3.25), и вычисляю сколько забрать "забрать_из_яблок_в_наличии" = 5.4-3.25=2.15, потом примерно такой запрос в базу:

Делаю запрос:

@mysql_query("UPDATE `".$apple_table."` SET

`apple_stock`='".($res_balance['apple_stock']-$withdraw_apple_stock)."', //3.25-3.25 =0

`apple_stock_eat`='".($res_balance['apple_stock_eat']+$withdraw_apple_stock)."', //0+3.25 = 3.25

`apple_balance`='".($res_balance['apple_balance']-$withdraw_apple_balance)."', //2.15-2.15 = -4.4408920985006E-16

`apple_balance_eat`='".($res_balance['apple_balance_eat']+$withdraw_apple_balance)."' //0+2.15 = 2.15

...

WHERE `driver`='777'

",$db);

В итоге такие результаты:

`apple_stock`=0

`apple_stock_eat`=3.25

`apple_balance`= -4.4408920985006E-16 //должно быть 0

`apple_balance_eat`=2.15

И вот в двух словах:

Получается одно и тоже число при минусовании коряво считается (в квадратных скобках, число которое "лагает" и было достато из базы):

2.25-[2.25]=-4.4408920985006E-16

0+[2.25]=2.25

Кстати если вместо него ввести вручную 2.25 в туже переменную, то всё работает норм, также с некоторыми другими цифрами всё работает норм. Думал может число коряво было записано в базе, но 40 попыток его туда впихнуть по разному приводят всёравно к такому корявому результату..

Слышал про стандарт php IEEE 754 и про проблемы с сравниванием и вычислением чисел с павающей точкой (float), но ответ как решить вопрос так и не нашел, ну и тут при замене числа на такое же не из базы - всё работает норм.

В чём фокус?? Вообще ещё помню не раз замечал как "по дороге" переменные забывают что в них находится, особенном при частом их использовании..

Как это пофиксить можно? Пробывал и floatval([2.25]) и далее делал echo и выводило 2.25 и в этомже echo минусовал 2.25-[2.25]=-4.4408920985006E-16, также пробывал if([2.25]==2.25) - такое условие не срабатывает.

P.S 7 часов сижу в этом ковыряюсь :popcorn:

http://www.youtube.com/watch?v=Gyl4E4EcPdc (http://www.youtube.com/watch?v=Gyl4E4EcPdc) - партнёрка от Мавроди и МММ))) http://miniwm.ru/soft.php?read=917 (http://miniwm.ru/soft.php?read=917) CMS MiniWM v2.8 - 49.99$
Artisan
На сайте с 04.03.2005
Offline
371
#1
FFFFx029A:
Слышал про стандарт php IEEE 754 и про проблемы с сравниванием и вычислением чисел с павающей точкой (float), но ответ как решить вопрос так и не нашел, ну и тут при замене числа на такое же не из базы - всё работает норм.

Числа float (с плавающей точкой) надо

сравнивать с допустимой погрешностью.

FFFFx029A:
Как это пофиксить можно? Пробывал и floatval([2.25]) и далее делал echo и выводило 2.25 и в этомже echo минусовал 2.25-[2.25]=-4.4408920985006E-16, также пробывал if([2.25]==2.25) - такое условие не срабатывает. P.S 7 часов сижу в этом ковыряюсь 🍿

Для обязательной точности бухгалтерских

вычислений есть специальные библиотеки

для вычислений с десятичными дробями.

Там есть типы переменных,

функции, и дальше по списку.

Или используйте язык для бухгалтеров,

в котором уже есть такие вычисления.

https://en.wikipedia.org/wiki/COBOL

COBOL is a compiled English-like

computer programming language

designed for business use.

www.leak.info / ДАРОМ линки конкурентов и забытых доменов
Оптимизайка
На сайте с 11.03.2012
Offline
396
#2

Тип данных в таблице - небось float? Яблоки с точностью до 38 знаков после запятой вешаете? :) Меняйте на decimal(15,3), читайте про float.

⭐ BotGuard (https://botguard.net) ⭐ — защита вашего сайта от вредоносных ботов, воровства контента, клонирования, спама и хакерских атак!
FFFFx029A
На сайте с 01.09.2007
Offline
142
#3

Оптимизайка:
Тип данных в таблице - небось float? Яблоки с точностью до 38 знаков после запятой вешаете? :) Меняйте на decimal(15,3), читайте про float.

Да я в ручную в базу пихал число 2.5 и 3.5 и 5.4, далее 5.4-3.5=2.5 и вот потом начались нюансы уже с этим "вычисленным" 2.5.

Вообще да, в базе float везде у меня. Поставил тип полей DECIMAL, теперь там дробные числа не сохраняются.. Ааааа, вот заметил прикол, при создании DECIMAL там было 10,0 формат, погуглил немного и поменял на 19,2

И о чудо, в базу всё записалось как надо, а вот на экран вывел результат "вычисления" из переменной и получилось -4.4408920985006E-16

Я так понял главное чтоб в базе норм было, а то что оно там в переменной коряво хранится эт можно забить болт на это?? В целом доволен, спасибо.



---------- Добавлено 30.06.2016 в 04:53 ----------

Artisan:
Числа float (с плавающей точкой) надо
сравнивать с допустимой погрешностью.



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

Там есть типы переменных,
функции, и дальше по списку.

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

https://en.wikipedia.org/wiki/COBOL

COBOL is a compiled English-like
computer programming language
designed for business use.

И как с допустимой погрешностью сравнить если 2.25==2.25 не работает? Этож элементарные числа вроде как..

Калькулятор же на компе работает норм? Та и в инете куча сайтов где реальные вычисления есть и всё без проблем..

Artisan
На сайте с 04.03.2005
Offline
371
#4
FFFFx029A:
Этож элементарные числа вроде как.

Если для десятичных дробей использовать

приближение двоичными дробями, то обязательно

будут погрешности вычисления и представления,

потому что десять не есть степень двойки.

Тип float есть двоичная дробь, поэтому он

не может точно представлять десятичные дроби.

Для точных десятичных вычислений

нужны специальные типы данных.

FFFFx029A
На сайте с 01.09.2007
Offline
142
#5
Artisan:
Если для десятичных дробей использовать
приближение двоичными дробями, то обязательно
будут погрешности вычисления и представления,
потому что десять не есть степень двойки.

Тип float есть двоичная дробь, поэтому он
не может точно представлять десятичные дроби.

Для точных десятичных вычислений
нужны специальные типы данных.

Ну теперь буду знать, спасибо. Если честно до этого не было надобности в точных подсчётах, возможно не замечал эти косяки.. В целом если юзать в базе DECIMAL с настройкой (19,2), то числа типа -4.4408920985006E-16 нормально сохраняются, получается если вывести на экран то что в переменной, то будет фигня, а вот в базу сохранилось как мне нужно 2.15

Уже в базе заменил все типа данных с FLOAT на DECIMAL, так что думаю норм будет.

Всем огромное спасибо. Реально 10 часов сидел ковырял это дело, мозг отключился. Всем добра и удачи!

C
На сайте с 04.02.2005
Offline
291
#6

Для финансовых (денежных) операций, лучше хранить в decimal(16,4), чтоб вдруг не потерять на конвертации валют

Но и там есть нюансы, которые редко учитывают

при сложении, умножении, округлении - могут теряться копейки

Оптимизайка
На сайте с 11.03.2012
Offline
396
#7
Chukcha:
при сложении, умножении, округлении - могут теряться копейки

Поэтому в финансовых системах деньги хранят в целых числах (в копейках) т.е., чтоб не терялись ;)

C
На сайте с 04.02.2005
Offline
291
#8

0,001

*

100

=

0,1

Верно

Но цены 0,001 не существует, и предстаалена в виде 0,01

0,01

*

100

=

1.00

О! Как надо работать!

FFFFx029A
На сайте с 01.09.2007
Offline
142
#9
Chukcha:
Для финансовых (денежных) операций, лучше хранить в decimal(16,4), чтоб вдруг не потерять на конвертации валют

Но и там есть нюансы, которые редко учитывают

при сложении, умножении, округлении - могут теряться копейки

Да мне всё что ниже сотых 0.01 - не нужно, я и так округляю в меньшую сторону)

Так что остановился на формате decimal(19,2)

Ещё мучал вопрос что за число -4.4408920985006E-16, оказывается это число 0, надеюсь с нормальными числами которые больше нуля - таких нюансов не будет..

Кстати вопрос, а если у меня при вычислениях вместо 7.77 выйдет число типа NaN, то при сохранение в базу оно сохранится нормально?

VHS
На сайте с 28.09.2007
Offline
142
VHS
#10

Храни в целых числах [x], показывай как хочешь - [x] / 100(1000, 10000)...

А версия mysql какая?

Тут неплохо разжевано все

http://tarlyun.com/blog/2011/03/22/xranenie-ne-celyx-chisel-v-mysql/

12

Авторизуйтесь или зарегистрируйтесь, чтобы оставить комментарий