В какой-то момент у меня был этот блок кода:
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 быть так оптимизирован для этого случая? Есть что-то, чего я не вижу?
Одна вещь, которую я должен был упомянуть, это то, что, хотя можно было бы ожидать, что второй метод даст лучшие результаты, благодаря простому вычитанию, он этого не сделал. Возможно, из-за деления в начале цикла (вот почему я спросил: «Может быть, fmod так оптимизирован)?
Согласно документации, 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.
Других решений пока нет …