Ответ 1
Попробуйте сделать это быстро:
$price_int = intval(floor($price_corrected + 0.5));
Проблема, с которой вы столкнулись, не является ошибкой PHP, все языки программирования, использующие реальные числа с арифметикой с плавающей запятой, имеют схожие проблемы.
Общее правило для денежных расчетов заключается в том, чтобы никогда не использовать float (ни в базе данных, ни в вашем script). Вы можете избежать всех проблем, всегда сохраняя центы вместо долларов. Центами являются целые числа, и вы можете свободно добавлять их вместе и умножать на другие целые числа. Всякий раз, когда вы показываете номер, убедитесь, что вы вставили точку перед двумя последними цифрами.
Причина, по которой вы получаете 114 вместо 115, заключается в том, что floor
округляется вниз, к ближайшему целому числу, поэтому floor (114.999999999) становится 114. Более интересный вопрос: почему 1,15 * 100 составляет 114.999999999 вместо 115. причина в том, что 1.15 не совсем соответствует 115/100, но это немного меньше, поэтому, если вы умножаетесь на 100, вы получаете число, меньшее, чем 115.
Вот более подробное объяснение, что echo 1.15 * 100;
делает:
- Он анализирует значение 1.15 на двоичное число с плавающей запятой. Это связано с округлением, это немного округляет, чтобы получить двоичное число с плавающей запятой, ближайшее к 1.15. Причина, по которой вы не можете получить точное число (без ошибки округления), состоит в том, что 1.15 имеет бесконечное число цифр в базе 2.
- Он анализирует 100 на двоичное число с плавающей запятой. Это включает округление, но поскольку 100 - маленькое целое число, ошибка округления равна нулю.
- Он вычисляет произведение двух предыдущих чисел. Это также требует небольшого округления, чтобы найти ближайшее двоичное число с плавающей запятой. Ошибка округления в этой операции равна нулю.
- Он преобразует двоичное число с плавающей запятой в десятичное число базы с точкой и печатает это представление. Это также связано с небольшим округлением.
Причина, по которой PHP печатает удивительный Corrected price = float(115)
(вместо 114.999...), состоит в том, что var_dump
не печатает точного числа (!), но печатает число, округленное до n - 2
(или n - 1
), где n цифр - это точность вычисления. Вы можете легко проверить это:
echo 1.15 * 100; # this prints 115
printf("%.30f", 1.15 * 100); # you 114.999....
echo 1.15 * 100 == 115.0 ? "same" : "different"; # this prints `different'
echo 1.15 * 100 < 115.0 ? "less" : "not-less"; # this prints `less'
Если вы печатаете поплавки, помните: вы не всегда видите все цифры при печати float.
См. также большое предупреждение в начале PHP float docs.