Intereting Posts
Получить день из строки на испанском языке PHP Идентификатор SyntaxError: начинается сразу после числового литерала. Передача переменной PHP в JavaScript PHP: простой HTML DOM Parser – несколько атрибутов в find ()? cURL ошибка 60: сертификат SSL в Laravel 5.4 Ошибка: данные усечены для столбца «lat» в строке 1 Представление формы Ajax в symfony2 с изящной деградацией для пользователей без javascript Определять ориентацию видео и автоматически поворачивать при необходимости Ошибка MySQL: Неизвестный столбец в разделе where Как хранить qrcode, сгенерированный с помощью phpqrcode, в db вместо пути к файлу? Преобразование денег в текст в PHP Объединение нескольких массивов, сортировка по количеству массивов Является ли хорошей практикой перенаправление после отправки формы? Такой же скрипт cURL работает в dev. Не могу понять, почему не в производстве Как запустить миграцию для определенной среды в laravel исключить атрибуты html в str_replace

php intval () и floor () возвращаемое значение слишком низкое?

Поскольку тип данных float в PHP неточен, а FLOAT в MySQL занимает больше места, чем INT (и является неточным), я всегда храню цены как INT, умножая на 100 до хранения, чтобы обеспечить ровно 2 десятичных знака точности , Однако я считаю, что PHP не прав. Пример кода:

echo "<pre>"; $price = "1.15"; echo "Price = "; var_dump($price); $price_corrected = $price*100; echo "Corrected price = "; var_dump($price_corrected); $price_int = intval(floor($price_corrected)); echo "Integer price = "; var_dump($price_int); echo "</pre>"; 

Производимая мощность:

 Price = string(4) "1.15" Corrected price = float(115) Integer price = int(114) 

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

 Price = string(4) "1.15" Corrected price = float(114.999999999) Integer price = int(114) 

что продемонстрировало бы неточность типа float. Но почему этаж (115) возвращается 114 ??

Попробуйте это быстро:

 $price_int = intval(floor($price_corrected + 0.5)); 

Проблема, с которой вы столкнулись, – это не ошибка PHP, все языки программирования с использованием реальных чисел с арифметикой с плавающей запятой имеют схожие проблемы.

Общее правило для денежных расчетов заключается в том, чтобы никогда не использовать float (ни в базе данных, ни в вашем скрипте). Вы можете избежать всех проблем, всегда сохраняя центы вместо долларов. Центами являются целые числа, и вы можете свободно добавлять их вместе и умножать на другие целые числа. Всякий раз, когда вы показываете номер, убедитесь, что вы вставили точку перед двумя последними цифрами.

Причина, по которой вы получаете 114 вместо 115, состоит в том, что 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' 

Если вы печатаете поплавки, помните: вы не всегда видите все цифры при печати поплавка .

См. Также большое предупреждение в начале PHP-плавающих документов.

По-моему, другие ответы затронули причину и хорошее обходное решение проблемы.

Стремясь устранить проблему под другим углом:

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

PHP делает округление на основе значительных цифр. Это скрывает неточность (в строке 2). Конечно, когда приходит пол, он не знает ничего лучшего и полностью останавливает его.

Возможно, это еще одно возможное решение для этой «проблемы»:

 intval(number_format($problematic_float, 0, '', '')); 

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

Решение состоит в том, чтобы убедиться, что когда вы работаете с значениями с плавающей запятой, и вам нужно поддерживать точность – используйте функции gmp или функции математики BC – bcpow, bcmul et al. и проблема будет решена легко.

Например, вместо $ price_corrected = $ price * 100;

используйте $ price_corrected = bcmul ($ price, 100);