массивы — PHP по модулю против вычитания в PHP_INT_MAX

В какой-то момент у меня был этот блок кода:

while( $i> $l-1 )
{
$x= fmod($i,$l);
$i= floor($i/$l);
}

Я решил избавиться от операции по модулю и написал этот блок:

while( true )
{
$d= floor( $i/$l );
if( $d>= 1 )
{
$x= $i - ($d*$l);
$i= $d;
}
else
{
break;
}
}

$ X используется для индексации массива длиной $ l. $ I здесь под вопросом.

В то время как для некоторого относительно небольшого начального $ i оба блока дают одинаковый $ x на всех итерациях, но при инициализации с чем-то близким к PHP_INT_MAX два блока не дают одинаковый $ x.

К сожалению, $ l не может стать степенью 2, чтобы использовать битовые операторы, поэтому я застрял с этим.

Я предполагаю, что это как-то связано с происходящими внутренними закруглениями. Может ли fmod быть так оптимизирован для этого случая? Есть что-то, чего я не вижу?

Дополнительный комментарий после принятия ответа @trincot.

Одна вещь, которую я должен был упомянуть, это то, что, хотя можно было бы ожидать, что второй метод даст лучшие результаты, благодаря простому вычитанию, он этого не сделал. Возможно, из-за деления в начале цикла (вот почему я спросил: «Может быть, fmod так оптимизирован)?

0

Решение

Согласно документации, fmod работает на поплавках:

fmod — возвращает остаток с плавающей запятой (по модулю) от деления аргументов

Вместо этого оператор по модулю (%) будет более подходящим для того, что вам нужно:

Операнды модуля перед обработкой преобразуются в целые числа (путем удаления десятичной части).

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

Примеры некоторых странностей, которые случаются:

$l=3;
$i=9223372036854775295;
echo is_int($i) . "<br>"; // 1 (true)
echo (9223372036854775295==$i) . "<br>"; // 1 (true)
echo number_format($i, 0, ".", "") . "<br>"; // 9223372036854774784
echo fmod($i,$l) . "<br>";   // 1
echo fmod($i-1,$l) . "<br>"; // 1
echo fmod($i-2,$l) . "<br>"; // 1
echo ($i % $l) . "<br>";     // 2
echo (($i-1) % $l) . "<br>"; // 1
echo (($i-2) % $l) . "<br>"; // 0

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

Обратите также внимание, что это отсутствие точности делает fmod верните 1 для трех последовательных чисел, в то время как оператор по модулю делает то, что вы хотите.

Таким образом, вы, кажется, намного лучше с %,

альтернатива

Кажется, ваша функция разбивает число на «цифры» в L-образной форме. Например, когда $l=2, ваш $x-последовательность производит двоичное представление числа, за исключением последней цифры, которую вы пропустите.

В этом отношении вы могли бы взглянуть на вызов функции base_convert($i,10,$l), который производит одну цифру, соответствующую значению $x в вашем коде, с буквами для цифр выше 9. Функция может принимать $l значения до 36.

1

Другие решения

Других решений пока нет …