[PHP] Сравнение одинаковых чисел не работает?
Вопрос к знатокам. Скорее всего я, что-то упускаю из виду…
Под катом вопросы…
Вопросы:
$var1 = floatval(995); // double 995
$var2 = floatval(19.9 * 50); // double 995
$var3 = floatval(1990 - floatval(19.9 * 50)); // double 995
$format = '%0' . "b\n";
printf($format, $var1); // 1111100011
printf($format, $var2); // 1111100010
printf($format, $var3); // 1111100011
print PHP_EOL . '===' . PHP_EOL;
print intval($var1 === $var2) . PHP_EOL; // false
print intval($var1 === $var3) . PHP_EOL; // false
print intval($var2 === $var3) . PHP_EOL; // false
print PHP_EOL . 'intval === intval' . PHP_EOL;
print intval(intval($var1) === intval($var2)) . PHP_EOL; // false
print intval(intval($var1) === intval($var3)) . PHP_EOL; // true
print intval(intval($var2) === intval($var3)) . PHP_EOL; // false
print PHP_EOL . 'round === round' . PHP_EOL;
print intval(round($var1, 2) === round($var2, 2)) . PHP_EOL; // true
print intval(round($var1, 2) === round($var3, 2)) . PHP_EOL; // true
print intval(round($var2, 2) === round($var3, 2)) . PHP_EOL; // true
Под катом вопросы…
Вопросы:
- Почему $var1 и $var3 в битах 1111100011, а $var2 — 1111100010?
- Почему $var1 и $var3 не равны (ни тождественно, ни обычно)?
- Почему только после обработки round-ом все 3 числа становятся равны?
Поблагодарить автора
Отправить деньги
Комментарии: 17
Неужели настолько незаметно?)
О чём речь? О памятке «Точность чисел с плавающей точкой»? Почему тогда:
print PHP_EOL . 'intval === intval' . PHP_EOL;
print intval(intval($var1) === intval($var2)) . PHP_EOL; // false
print intval(intval($var1) === intval($var3)) . PHP_EOL; // true
print intval(intval($var2) === intval($var3)) . PHP_EOL; // false
?
php.net/manual/ru/language.types.float.php
Кроме того, рациональные числа, которые могут быть точно представлены в виде чисел с плавающей точкой с основанием 10, например, 0.1 или 0.7, не имеют точного внутреннего представления в качестве чисел с плавающей точкой с основанием 2, вне зависимости от размера мантиссы. Поэтому они и не могут быть преобразованы в их внутреннюю двоичную форму без небольшой потери точности. Это может привести к неожиданным результатам: например, floor((0.1+0.7)*10) скорее всего вернет 7 вместо ожидаемого 8, так как результат внутреннего представления будет чем-то вроде 7.9999999999999991118....php.net/manual/ru/language.types.integer.php#language.types.integer.casting
При преобразовании из float в integer, число будет округлено в сторону нуля.floatval(19.9 * 50) по факту чуть меньше чем 995, а floatval(1990 — floatval(19.9 * 50)) чуть больше (в десятичном представлении), при intval число округляется в строну 0, поэтому var2 становится 994,var3 = 995, отсюда и результаты
print intval($var1) > intval($var2); //true
Спасибо, понятно!
а так если?
$var1 = (float) 995;
$var2 = (float) bcmul(19.9, 50);
Не проверял. Меня не устраивает, что необходимо использовать стороннюю библиотеку для деления числа на число, учитывая то, что я делаю продукт для массового использования… Да и проблему для себя я решил, однако хочется понять, почему такое происходит.
да оно вроде давно встроено в пых еще с PHP 5.0.0. вроде
Странно, а у меня нет…
Начиная с PHP 4.0.4, libbcmath встроена в PHP. Расширение не требует внешних библиотек.
Угу, знаю теперь… а у меня чёт он не поддерживается…
(float) быстрее в 6 раз, чем floatval()
вроде бы
вроде бы
Не в этом суть. Floatval использовал в примере лишь для наглядности.
Кроме того, рациональные числа, которые могут быть точно представлены в виде чисел с плавающей точкой с основанием 10, например, 0.1 или 0.7, не имеют точного внутреннего представления в качестве чисел с плавающей точкой с основанием 2, вне зависимости от размера мантиссы. Поэтому они и не могут быть преобразованы в их внутреннюю двоичную форму без небольшой потери точности. Это может привести к неожиданным результатам: например, floor((0.1+0.7)*10) скорее всего вернет 7 вместо ожидаемого 8, так как результат внутреннего представления будет чем-то вроде 7.9999999999999991118....
На деле так оно и есть.
php > printf('%d', 19.9*50);
994
php > printf('%f', 19.9*50);
995.000000
Пример из документации такой же:
php > printf('%d', (0.1+0.7) * 10);
7
php > printf('%f', (0.1+0.7) * 10);
8.000000
Как решение, и так мы и делали, когда писали биллинг, это всегда приводить правильно к типу, т.е. работать всегда явно с float.
Спасибо. Теперь ясно стало.
Обсуждали как-то похожее в чате СПб Фронтэнд, возможно будет интересна эта статья habrahabr.ru/post/112953/
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.