Определите, делится ли $x равномерно на $y в PHP
Я просто хочу знать, если $x равномерно делится на $y. например, предположим:
$x = 70;
$y = .1;
Первое, что я попробовал, это:
$x % $y
Это, кажется, работает, когда оба числа являются целыми числами, но терпит неудачу, если это не так, и если $y
десятичное число меньше 1 возвращает ошибку "деление на ноль", поэтому я попытался:
fmod($x,$y)
Который возвращает столь же запутанные результаты, "0.09999999999996".
Php.net состояния fmod()
:
Возвращает остаток с плавающей запятой от деление дивиденда (x) на делитель (y)
Ну, согласно моему калькулятору 70 / .1 = 700
. Что означает, что остаток равен 0. Может кто-нибудь объяснить, что я делаю не так?
5 ответов:
Одним из решений будет выполнение нормальногоделения , а затем сравнение значения со следующим целым числом. Если результат равен этому целому числу или очень близок к нему, то результат делится равномерно:
Это вычитает оба числа и проверяет, что абсолютная разница меньше, чем определенная ошибка округления. Это обычный способ сравнения чисел с плавающей точкой, так как в зависимости от того, как вы получили поплавок, представление может меняться:$x = 70; $y = .1; $evenlyDivisable = abs(($x / $y) - round($x / $y, 0)) < 0.0001;
php> 0.1 + 0.1 + 0.1 == 0.3 bool(false) php> serialize(.3) 'd:0.29999999999999999;' php> serialize(0.1 + 0.1 + 0.1) 'd:0.30000000000000004;'
Смотрите это демо:
php> $x = 10; int(10) php> $y = .1; double(0.1) php> abs(($x / $y) - round($x / $y, 0)) < 0.0001; bool(true) php> $y = .15; double(0.15) php> abs(($x / $y) - round($x / $y, 0)) < 0.0001; bool(false)
.1 не имеет точного представления в двоичной системе счисления с плавающей запятой, что и приводит к неверному результату. Вы можете умножить их на достаточно большую степень 10, чтобы они были целыми числами, затем использовать%, а затем преобразовать обратно. Это зависит от того, что они не отличаются достаточно большим фактором, что умножение на степень 10 приводит к переполнению/потере точности одного из них. Вот так:
$x = 70; $y = .1; $factor = 1.0; while($y*$factor != (int)($y*$factor)){$factor*=10;} echo ($x*$factor), "\n"; echo ($y*$factor), "\n"; echo (double)(($x*$factor) % ($y*$factor))/$factor;
Есть чисто математическая библиотека в системе Bitbucket : https://bitbucket.org/zdenekdrahos/bn-php
Тогда решение будет следующим:
php > require_once 'bn-php/autoload.php'; php > $eval = new \BN\Expression\ExpressionEvaluator(); php > $operators = new \BN\Expression\OperatorsFactory(); php > $eval->setOperators($operators->getOperators(array('%'))); php > echo $eval->evaluate('70 % 0.1'); // 0 0.00000000000000000000
Проверено на php5.3
Кредиты: http://www.php.net/manual/en/function.bcmod.php#111276
Представление с плавающей точкой варьируется от машины к машине. К счастью, есть стандарты. PHP обычно использует формат двойной точности IEEE 754 для представления с плавающей запятой, который является одним из наиболее распространенных стандартов. смотрите здесь для получения дополнительной информации об этом. С учетом сказанного взгляните наэтот калькулятор для лучшего понимания того, почемуПочему . Что касается как Мне нравится решение Тима, особенно если вы имеете дело с пользовательским вводом.
Как вы сказали, использование оператора модуля прекрасно работает, когда это целое число, так почему бы не настроить его так, чтобы он работал с целыми числами. В моем случае мне нужно было проверить делимость на 0,25:
$input = 5.251 $x = round($input, 3); // round in case $input had more decimal places $y = .25; $result = ($x * 1000) % ($y * 1000);
В вашем случае:
$input = 70.12 $x = round($input, 2); $y = .1; $result = ($x * 100) % ($y * 100);