Greg Hewgill и 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 будет идти, но останавливаться раньше, тем самым увеличивая скорость вычислений.
person
BJovke
schedule
13.02.2017
i = * ( long * ) &y;
адрес y берется как указатель на long, а затем снова разыменовывается? - person Nubcake   schedule 27.07.2017y
- этоfloat
, и это переводит его в целое число. (Небезопасно, потому что это нарушает правила строгого псевдонима C. Aunion
в C99 илиmemcpy
в C89 / C ++ будет делать то же самое, следуя правилам языка, и компилирует то же самое, по крайней мере, с современными оптимизирующими компиляторами.) - person Peter Cordes   schedule 14.12.2017