Удивительное преобразование float-int в PHP - PullRequest
1 голос
/ 13 апреля 2011

Потратив час, пытаясь выяснить, почему PHP-скрипт дал мне неправильный вывод, оказалось, что цикл в одной конкретной ситуации выполнялся на одну итерацию коротким.
Чтобы объяснить, что происходит: номер версии (2 цифры, например, 1,5 или 2,0) считывается из атрибута XML и умножается на 100. Позднее сценарий выполняет итерации по определенному диапазону, кратному числу версий.

Получается, что 410 == 409, что вызывает удивление, если сравнивать счетчик с шагом 10 с этим значением.

Что подводит меня к моему вопросу: понимаю ли я что-то неправильно? Конечно, 4.1, 100 и 410 должны быть хорошо представимы как float и должны быть хорошо конвертируемыми в int без ошибок округления?

Однако в моей системе (с CLI PHP 5.3.2, Zend Engine 2.3.0) следующий тестовый пример

<?
$a = 100 * 4.1;
$b = (string) $a;
$c = (int) $a;
$d = (int)(string) $a;

var_dump($a);
var_dump($b);
var_dump($c);
var_dump($d);
?>

выходы:

float(410)
string(3) "410"
int(409)
int(410)

Сейчас я делаю (int)(string) конвертацию, которая работает, но это своего рода неприятный хак, который не очень красив и не совсем подходит.
Есть ли лучшее (правильное, без взлома) решение для получения точного результата?

Ответы [ 5 ]

3 голосов
/ 16 октября 2014

Вы можете использовать математические функции BC для получения предсказуемых результатов умножения чисел с плавающей запятой

http://php.net/manual/en/ref.bc.php

intval(4.1 * 100) === 409
bcmul(4.1, 100) === '410'
3 голосов
/ 13 апреля 2011

Ваша проблема в первой строке:

$a = 100 * 4.1;

Литерал с плавающей точкой 4.1 фактически не имеет значения 4,1 , поэтому результат умножения его на 100 не 410, а немного меньше. Видимо, приведение к строковым округлениям вверх, но приведение к int округляется вниз.

Чтобы получить ожидаемую десятичную математику, используйте функции BC Math .

2 голосов
/ 13 апреля 2011

Попробуйте использовать intval

$c = intval($a);

Edit:

Попробуйте округлить его, используя обычное округление:

$c = round($a, 0, PHP_ROUND_HALF_UP);
2 голосов
/ 13 апреля 2011

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

1 голос
/ 13 апреля 2011

Умножение на 4.1 вернет число с плавающей запятой, как вы уже видели. Приведение float к int будет делать что-то вроде floor (), это означает, что если результат был 409.999 ... он вернет 409. Если вы хотите работать с целыми числами, вы не должны либо смешивать значения float и integer в вычислениях, либо Вы должны использовать round () впоследствии, чтобы получить ближайшее целое число.

Возможные значения для точного представления будут 0,5, 0,25, 0,125, значения, которые можно получить с делениями на 2, поскольку значение хранится в двоичной системе.

...