Грег Хьюгилл и IllidanS4 дали ссылку с превосходным математическим объяснением.
Я постараюсь обобщить это здесь для тех, кто не хочет вдаваться в подробности.
Любая математическая функция, за некоторыми исключениями, может быть представлена полиномиальной суммой:
y = f(x)
может быть точно преобразовано в:
y = a0 + a1*x + a2*(x^2) + a3*(x^3) + a4*(x^4) + ...
Где a0, a1, a2, ... являются константами . Проблема состоит в том, что для многих функций, таких как квадратный корень, для точного значения эта сумма имеет бесконечное число членов, она не заканчивается на некотором x ^ n . Но если мы остановимся на некотором x ^ n , мы все равно получим результат с некоторой точностью.
Итак, если у нас есть:
y = 1/sqrt(x)
В этом конкретном случае они решили отбросить все полиномиальные элементы выше секунды, вероятно, из-за скорости вычисления:
y = a0 + a1*x + [...discarded...]
И теперь задача сводится к вычислению a0 и a1, чтобы у y было наименьшее отличие от точного значения. Они подсчитали, что наиболее подходящие значения:
a0 = 0x5f375a86
a1 = -0.5
Итак, когда вы положите это в уравнение, вы получите:
y = 0x5f375a86 - 0.5*x
То же самое, что строка, которую вы видите в коде:
i = 0x5f375a86 - (i >> 1);
Редактировать: на самом деле здесь y = 0x5f375a86 - 0.5*x
- это не то же самое, что i = 0x5f375a86 - (i >> 1);
, поскольку смещение числа с плавающей точкой как целого не только делит на два, но также делит экспоненту на два и вызывает некоторые другие артефакты, но все же сводится к вычислению некоторые коэффициенты a0, a1, a2 ....
В этот момент они обнаружили, что точности этого результата недостаточно для этой цели. Поэтому они дополнительно сделали только один шаг итерации Ньютона, чтобы улучшить точность результата:
x = x * (1.5f - xhalf * x * x)
Они могли бы сделать еще несколько итераций в цикле, каждый из которых улучшал бы результат, пока не была достигнута требуемая точность. Именно так оно и работает в CPU / FPU! Но, похоже, достаточно было только одной итерации, что также было благословением для скорости. CPU / FPU выполняет столько итераций, сколько необходимо для достижения точности числа с плавающей запятой, в котором сохраняется результат, и имеет более общий алгоритм, который работает для всех случаев.
Короче говоря, они сделали следующее:
Используйте (почти) тот же алгоритм, что и CPU / FPU, используйте улучшение начальных условий для особого случая 1 / sqrt (x) и не рассчитывайте весь путь к точности, с которой будет идти CPU / FPU но раньше останавливаться, тем самым увеличивая скорость расчета.