Если a > b > 1 и x > 1, то log_a(x) должен быть меньше log_b(x). Это очевидно: чем больше основание, тем меньше показатель степени нужен, чтобы получить то же x. Но PHP и Lua считают иначе. Возьмите x = 2.93, a = 10 + 2⁻⁴⁹, b = 10. Выполните assert($a > $b) и проверьте логи: var_dump(log($x, $a) < log($x, $b)) печатает false, а var_dump(log($x, $a) == log($x, $b)) — true. Выходит, два логарифма равны, хотя основание разное и a строго больше b. Это не рядовое плавающее округление — на тех же числах Python выдаёт предсказуемое равенство, а тут результат словно переворачивается.
Разгадка кроется в реализации log с произвольным основанием. Обычно языки вычисляют такой логарифм как ln(x) / ln(a). Но в libm нет готовой функции под любой base, зато есть отдельные log10 и log2. PHP и Lua захотели как лучше: когда base равен 10 или 2, они дёргают log10 или log2 напрямую, избегая двойного округления от логарифма и деления. Для прочих оснований — честно считают отношение натуральных логарифмов. В нашем примере a = 10 + 2⁻⁴⁹ обрабатывается через ln(x)/ln(a), а b = 10 — через log10(x). Это два разных механизма, и их погрешности расходятся. Разрыв на границе base = 10 ломает монотонность, которая выполнялась бы для каждого метода по отдельности. Причём ln(a) и ln(b) из-за округления совпадают до последнего бита, но результат деления всё равно отличается от вызова log10.
Автор называет это не столько багом, сколько недодуманной оптимизацией. В Lua в версии 5.2 поступили ровно наоборот правильному пути: добавили math.log10 и… убрали специальный случай из math.log. Логичнее было оставить быстрый math.log10 для всех, а у math.log убрать особые ветки, чтобы люди, работающие с произвольным основанием, не сталкивались с неприятным краевым эффектом. PHP тоже грешит: имея отдельную log10, он всё равно хардкодит базы 10 и 2 внутри log. Бонусом PHP и C# зачем-то возвращают NaN на log(x, 1), а Lua и Python — ±Inf. Отдельная головная боль — значение по умолчанию: сигнатура PHP утверждает, что база по умолчанию — M_E, но log(x) на деле вычисляет натуральный логарифм, и log(x, M_E) дал бы тот же результат только из-за грубого округления дроби ln(x)/ln(a). Вся эта неразбериха лишний раз показывает, как бездумные попытки «улучшить» строгую модель IEEE-754 порождают сюрпризы, которые потом списывают на врождённую неточность чисел с плавающей запятой.