Плавающий в int, изменяющий значение простого числа в PHP [дубликат]


На этот вопрос уже есть ответ здесь:

Я использую класс Money, для создания экземпляра которого требуется, чтобы вся валюта находилась в int. Случилось то, что я наткнулся на определенный номер (я пробовал использовать множество других номеров, и все работает так, как ожидалось) что, когда тип приведен от float к int, имеет другое значение.

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

$value = (float)9.78;
var_dump($value );
$prec = (int)(100);
var_dump($prec);
$value = $value * $prec;
var_dump($value);
var_dump((int)($value));

... который выдает следующий результат...

float(9.78)  /* $value as a float */
int(100)     /* $prec as an int */
float(978)   /* $value * $prec as a float, all going well... */
int(977)     /* $value type cast to an int ???????? */

... что, черт возьми, здесь происходит? Почему $value тип приведен к int в этом сценарии с другим ценность?


РЕДАКТИРОВАТЬ: Причина, по которой я не принимаю это как дубликат, связана с тем, что ответ, в котором я нуждался, не присутствовал в другом потоке. Вот оно: я должен был подать заявление round() вот так...

$dec_precision = strlen((string)($prec)-1);
$value = round($value * $prec, $dec_precision);

Надеюсь, это кому-то поможет!

Author: oucil, 2015-09-26

1 answers

PHP использует экспоненциальные представления чисел с плавающей запятой, поэтому он не является точным. Читать документы:

Числа с плавающей запятой имеют ограниченную точность. Хотя это зависит от системы, PHP обычно использует формат двойной точности IEEE 754, который даст максимальную относительную ошибку из-за округления порядка 1.11e-16. Неэлементарные арифметические операции могут давать большие ошибки и, конечно же, распространение ошибок необходимо учитывать, когда несколько операции усложняются.

Кроме того, рациональные числа, которые точно представимы в виде чисел с плавающей запятой в базе 10, например 0.1 или 0.7, не имеют точного представления в виде чисел с плавающей запятой в базе 2, которая используется внутри, независимо от размера мантиссы. Следовательно, они не могут быть преобразованы в свои внутренние двоичные аналоги без небольшой потери точности. Это может привести к запутанным результатам: например, пол((0,1+0,7)*10) обычно будет верните 7 вместо ожидаемых 8, так как внутреннее представление будет примерно таким 7.9999999999999991118....

Обновление 1:

Примечание PHP математические расширения: BCMath и GMP.

 2
Author: BlitZ, 2015-09-26 07:23:26