Jump to content

Быстрый обратный квадратный корень

Это хорошая статья. Нажмите здесь для получения дополнительной информации.
В расчетах освещения и отражения, как в видеоигре OpenArena , используется быстрый код обратного квадратного корня для вычисления углов падения и отражения.

Быстрый обратный квадратный корень , иногда называемый Быстрый InvSqrt() или шестнадцатеричная константа 0x5F3759DF — это алгоритм, который оценивает , обратное (или мультипликативное обратное) квадратному корню из 32-битного с плавающей запятой . числа в формате с плавающей запятой IEEE 754 . Алгоритм наиболее известен благодаря своей реализации в 1999 году в Quake III Arena , видеоигре- шутере от первого лица, в значительной степени основанной на 3D-графике . С последующими усовершенствованиями аппаратного обеспечения, особенно с x86 SSE. инструкцией rsqrtss, этот алгоритм вообще не лучший выбор для современных компьютеров, [1] хотя это остается интересным историческим примером. [2]

Алгоритм принимает на вход 32-битное число с плавающей запятой и сохраняет уменьшенное пополам значение для дальнейшего использования. Затем, рассматривая биты, представляющие число с плавающей запятой, как 32-битное целое число, выполняется логический сдвиг вправо на один бит и результат вычитается из числа. 0x 5F3759DF , который представляет собой представление с плавающей запятой приближения . [3] Это приводит к первому приближению обратного квадратного корня входных данных. Снова рассматривая биты как числа с плавающей запятой, он запускает одну итерацию метода Ньютона , давая более точное приближение.

Уильям Кахан и К.К. Нг из Беркли в мае 1986 года написали неопубликованную статью, описывающую, как вычислить квадратный корень, используя методы битовой игры с последующими итерациями Ньютона. [4] В конце 1980-х годов Клив Молер из Ardent Computer. об этой технике узнал [5] и передал его своему коллеге Грегу Уолшу. Грег Уолш разработал ныне известный константный и быстрый алгоритм обратного квадратного корня. Гэри Таролли консультировал Kubota, компанию, которая в то время финансировала Ardent, и, вероятно, представил алгоритм в 3dfx Interactive примерно в 1994 году. [6] [7]

Джим Блинн продемонстрировал простую аппроксимацию обратного квадратного корня в колонке журнала IEEE Computer Graphics and Applications в 1997 году . [8] Обратное проектирование других современных 3D-видеоигр выявило вариацию алгоритма в игре от Activision 1997 года Interstate '76 . [9]

Quake III Arena , видеоигра- шутер от первого лица , была выпущена в 1999 году компанией id Software и использовала этот алгоритм. Брайан Хук, возможно, перенес этот алгоритм из 3dfx в id Software. [6] Обсуждение кода появилось на китайском форуме разработчиков CSDN в 2000 году. [10] а Usenet и форум gamedev.net широко распространили код в 2002 и 2003 годах. [11] Возникли предположения относительно того, кто написал алгоритм и как была получена константа; некоторые предполагали, что это Джон Кармак . [7] Quake III был Полный исходный код представлен на QuakeCon 2005 , но ответов на него не было. Ответ на вопрос об авторстве был получен в 2006 году, когда Грег Уолш связался с Beyond3D, поскольку их предположения стали популярными на Slashdot. [6]

В 2007 году алгоритм был реализован в некоторых специализированных аппаратных вершинных шейдерах с использованием программируемых пользователем вентильных матриц (FPGA). [12] [13]

Мотивация

[ редактировать ]
Нормали поверхности широко используются в расчетах освещения и затенения, что требует расчета норм для векторов. Здесь показано поле векторов, нормальных к поверхности.
Двумерный пример использования нормали найти угол отражения по углу падения; в данном случае — на свете, отражающемся от изогнутого зеркала. Быстрый обратный квадратный корень используется для обобщения этого расчета на трехмерное пространство.

Обратный квадратный корень из числа с плавающей запятой используется в цифровой обработке сигналов для нормализации вектора, масштабирования его до длины 1 для получения единичного вектора . [14] Например, программы компьютерной графики используют обратные квадратные корни для вычисления падения и отражения света углов и затенения . Программы трехмерной графики должны выполнять миллионы таких вычислений каждую секунду, чтобы имитировать освещение. Когда код был разработан в начале 1990-х годов, большая часть вычислительной мощности с плавающей запятой отставала от скорости обработки целых чисел. [7] Это было проблематично для программ 3D-графики до появления специализированного оборудования для обработки преобразований и освещения . Вычисление квадратных корней обычно зависит от множества операций деления, которые для чисел с плавающей запятой требуют больших вычислительных затрат . Быстрый обратный квадрат дает хорошее приближение всего за один шаг деления.

Длина вектора определяется путем вычисления его евклидовой нормы : квадратного корня из суммы квадратов компонент вектора . Когда каждый компонент вектора делится на эту длину, новый вектор будет единичным вектором, указывающим в том же направлении. В программе трехмерной графики все векторы находятся в трехмерном пространстве , поэтому был бы вектор .

– евклидова норма вектора.

– нормированный (единичный) вектор. Замена , уравнение также можно записать как:

который связывает единичный вектор с обратным квадратным корнем компонентов расстояния. Обратный квадратный корень можно использовать для вычисления поскольку это уравнение эквивалентно

где член дроби представляет собой обратный квадратный корень из .

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

Обзор кода

[ редактировать ]

Следующий код представляет собой быструю реализацию обратного квадратного корня из Quake III Arena , лишенную директив препроцессора C , но включающую точный исходный текст комментария: [15]

float Q_rsqrt(float number)
{
  long i;
  float x2, y;
  const float threehalfs = 1.5F;

  x2 = number * 0.5F;
  y  = number;
  i  = * ( long * ) &y;                       // evil floating point bit level hacking
  i  = 0x5f3759df - ( i >> 1 );               // what the fuck?
  y  = * ( float * ) &i;
  y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
  // y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

  return y;
}

В то время общий метод вычисления обратного квадратного корня заключался в вычислении приближения для , затем пересмотрите это приближение с помощью другого метода, пока оно не окажется в допустимом диапазоне ошибок фактического результата. Распространенные программные методы начала 1990-х годов основывались на справочной таблице . [16] Ключом к быстрому обратному квадратному корню было непосредственное вычисление аппроксимации с использованием структуры чисел с плавающей запятой, что оказалось быстрее, чем поиск в таблице. Алгоритм оказался примерно в четыре раза быстрее, чем вычисление квадратного корня другим методом и вычисление обратной величины посредством деления с плавающей запятой. [17] Алгоритм был разработан с учетом спецификации 32-битных чисел с плавающей запятой стандарта IEEE 754-1985 , но исследование Криса Ломонта показало, что он может быть реализован и в других спецификациях чисел с плавающей запятой. [18]

Преимущества в скорости, обеспечиваемые быстрым трюком с обратным квадратным корнем, возникли благодаря обработке 32-битного с плавающей запятой . слова [примечание 1] как целое число , а затем вычитая его из « магической » константы, 0x5F3759DF . [7] [19] [20] [21] Это вычитание целого числа и битовый сдвиг приводят к образованию битового шаблона, который, если его переопределить как число с плавающей запятой, является грубым приближением обратного квадратного корня из числа. Для достижения некоторой точности выполняется одна итерация метода Ньютона, и код готов. Алгоритм генерирует достаточно точные результаты, используя уникальное первое приближение метода Ньютона ; однако это намного медленнее и менее точно, чем использование SSE . инструкции rsqrtss на процессорах x86, также выпущенных в 1999 году. [1] [22]

Рабочий пример

[ редактировать ]

Например, число можно использовать для расчета . Первые шаги алгоритма показаны ниже:

0011_1110_0010_0000_0000_0000_0000_0000  Bit pattern of both x and i
0001_1111_0001_0000_0000_0000_0000_0000  Shift right one position: (i >> 1)
0101_1111_0011_0111_0101_1001_1101_1111  The magic number 0x5F3759DF
0100_0000_0010_0111_0101_1001_1101_1111  The result of 0x5F3759DF - (i >> 1)

Интерпретация как 32-битное представление IEEE:

0_01111100_01000000000000000000000  1.25 × 2−3
0_00111110_00100000000000000000000  1.125 × 2−65
0_10111110_01101110101100111011111  1.432430... × 263
0_10000000_01001110101100111011111  1.307430... × 21

Переинтерпретация этого последнего битового шаблона как числа с плавающей запятой дает приближение , что имеет погрешность около 3,4%. После одной единственной итерации метода Ньютона окончательный результат: , ошибка всего 0,17%.

Как избежать неопределенного поведения

[ редактировать ]

Согласно стандарту C, переинтерпретация значения с плавающей запятой как целого числа путем приведения и разыменования указателя на него недопустима ( неопределенное поведение ). Другой способ — поместить значение с плавающей запятой в анонимное объединение , содержащее дополнительный 32-битный целочисленный элемент без знака, и доступ к этому целому числу обеспечивает представление на уровне битов содержимого значения с плавающей запятой. Однако игра слов типов через объединение также является неопределенным поведением в C++.

# include <stdint.h> // uint32_t

float Q_rsqrt(float number)
{
  union {
    float    f;
    uint32_t i;
  } conv = { .f = number };
  conv.i  = 0x5f3759df - (conv.i >> 1);
  conv.f *= 1.5F - (number * 0.5F * conv.f * conv.f);
  return conv.f;
}

В современном C++ рекомендуемый метод реализации приведения этой функции — использование C++20. std::bit_cast. Это также позволяет функции работать в constexpr контекст:

import std;

constexpr std::float32_t Q_rsqrt(std::float32_t number) noexcept
{
  const auto y = std::bit_cast<std::float32_t>(
    0x5f3759df - (std::bit_cast<std::uint32_t>(number) >> 1));
  return y * (1.5f32 - (number * 0.5f32 * y * y));
}

Алгоритм

[ редактировать ]

Алгоритм вычисляет выполнив следующие шаги:

  1. Псевдоним аргумента в целое число как способ вычисления приближения двоичного логарифма
  2. Используйте это приближение, чтобы вычислить приближение
  3. Псевдоним обратного значения с плавающей запятой, как способ вычисления аппроксимации экспоненты по основанию 2.
  4. Уточните приближение, используя одну итерацию метода Ньютона.

Представление с плавающей запятой

[ редактировать ]

Поскольку этот алгоритм в значительной степени опирается на представление чисел с плавающей запятой одинарной точности на уровне битов, здесь представлен краткий обзор этого представления. Чтобы закодировать ненулевое действительное число как число с плавающей запятой одинарной точности, первым шагом будет запись как нормализованное двоичное число: [23]

где показатель степени является целым числом, и является двоичным представлением мантиссы . Поскольку единственный бит перед точкой в ​​мантиссе всегда равен 1, его не нужно сохранять. Уравнение можно переписать как:

где означает , так . Из этой формы вычисляются три целых числа без знака: [24]

  • , «знаковый бит» если является положительным и отрицательный или нулевой (1 бит)
  • - «смещенный показатель степени», где это « смещение показателя » [примечание 2] (8 бит)
  • , где [примечание 3] (23 бита)

Затем эти поля упаковываются слева направо в 32-битный контейнер. [25]

В качестве примера рассмотрим еще раз число . Нормализация дает:

и, таким образом, три целочисленных поля без знака:

эти поля упакованы, как показано на рисунке ниже:

Псевдоним целого числа как приближенный логарифм

[ редактировать ]

Если должны были быть рассчитаны без компьютера или калькулятора, была бы полезна таблица логарифмов вместе с тождеством , которое справедливо для любой базы . Быстрый обратный квадратный корень основан на этом тождестве, а также на том факте, что псевдоним float32 для целого числа дает грубое приближение его логарифма. Вот как:

Если положительное нормальное число :

затем

и поскольку , логарифм в правой части можно аппроксимировать выражением [26]

где — свободный параметр, используемый для настройки аппроксимации. Например, дает точные результаты на обоих концах интервала, в то время как дает оптимальное приближение (лучшее в смысле равномерной нормы ошибки). Однако это значение не используется алгоритмом, поскольку не учитывает последующие шаги.

Целое число, связанное с числом с плавающей запятой (синим цветом), по сравнению с масштабированным и сдвинутым логарифмом (серым цветом).

Таким образом, существует приближение

Интерпретация битового шаблона с плавающей запятой как целое число урожайность [примечание 4]

Тогда оказывается, что представляет собой масштабированную и сдвинутую кусочно-линейную аппроксимацию , как показано на рисунке справа. Другими словами, аппроксимируется

Первое приближение результата

[ редактировать ]

Расчет основано на тождестве

Используя приведенное выше приближение логарифма, примененное к обоим и , приведенное выше уравнение дает:

Таким образом, приближение является:

который записан в коде как

i  = 0x5f3759df - ( i >> 1 );

Первое слагаемое выше — это магическое число

из чего можно сделать вывод, что . Второй срок, , вычисляется путем сдвига битов на одну позицию вправо. [27]

метод Ньютона

[ редактировать ]
Относительная ошибка между прямым вычислением и быстрым обратным квадратным корнем при выполнении 0, 1, 2, 3 и 4 итераций метода поиска корня Ньютона. Обратите внимание, что используется двойная точность, и наименьшая представимая разница между двумя числами двойной точности достигается после выполнения 4 итераций.

С как обратный квадратный корень, . Аппроксимация, полученная на предыдущих этапах, может быть уточнена с помощью метода поиска корня — метода, который находит ноль функции . Алгоритм использует метод Ньютона : если есть приближение, для , то лучшее приближение можно вычислить, взяв , где является производной от в . [28] Для вышесказанного ,

где и .

Лечение как число с плавающей запятой, y = y*(threehalfs - x/2*y*y); эквивалентно

Повторив этот шаг, используя выходные данные функции ( ) в качестве входных данных следующей итерации алгоритм вызывает сходиться к обратному квадратному корню. [29] Для целей Quake III движка использовалась только одна итерация. Вторая итерация осталась в коде, но была закомментирована . [21]

Точность

[ редактировать ]

Как отмечалось выше, приближение очень точное. На единственном графике справа показана ошибка функции (то есть ошибка аппроксимации после того, как она была улучшена за счет выполнения одной итерации метода Ньютона) для входных данных, начинающихся с 0,01, где стандартная библиотека дает в результате 10,0. , а InvSqrt() дает 9,982522, что составляет относительную разницу 0,0017478, или 0,175% от истинного значения, равного 10. С этого момента абсолютная ошибка только падает, а относительная ошибка остается в тех же пределах во всех порядках величины.

Последующие улучшения

[ редактировать ]

Магическое число

[ редактировать ]

Точно неизвестно, как было определено точное значение магического числа. Крис Ломонт разработал функцию, позволяющую минимизировать ошибку аппроксимации путем выбора магического числа. по диапазону. Сначала он вычислил оптимальную константу для шага линейного приближения как 0x5F37642F , близко к 0x5F3759DF , но эта новая константа давала немного меньшую точность после одной итерации метода Ньютона. [30] Затем Ломонт искал постоянный оптимум даже после одной и двух итераций Ньютона и нашел 0x5F375A86 , что точнее оригинала на каждом этапе итерации. [30] В заключение он задал вопрос, было ли точное значение исходной константы выбрано путем вывода или методом проб и ошибок . [31] Ломонт сказал, что магическое число для 64-битного типа размера IEEE754 double равно 0x5FE6EC85E7DE30DA , но позже Мэтью Робертсон показал, что это именно так. 0x5FE6EB50C7B537A9 . [32]

Ян Кадлец уменьшил относительную ошибку еще в 2,7 раза, также скорректировав константы в одной итерации метода Ньютона: [33] прибытие после исчерпывающего поиска в

conv.i = 0x5F1FFFF9 - ( conv.i >> 1 );
conv.f *= 0.703952253f * ( 2.38924456f - x * conv.f * conv.f );
return conv.f;

Полный математический анализ для определения магического числа теперь доступен для чисел с плавающей запятой одинарной точности. [34] [35]

Нулевой результат

[ редактировать ]

Промежуточным по сравнению с использованием одной и двух итераций метода Ньютона с точки зрения скорости и точности является одна итерация метода Галлея . В этом случае метод Галлея эквивалентен применению метода Ньютона с исходной формулой . Затем этап обновления

где реализация должна вычислять только один раз, через временную переменную.

Устаревание

[ редактировать ]

Последующие дополнения производителей оборудования сделали этот алгоритм по большей части излишним. Например, на x86 Intel представила SSE инструкцию rsqrtss в 1999 году. В тесте Intel Core 2 2009 года эта инструкция занимала 0,85 нс на одно число с плавающей запятой по сравнению с 3,54 нс для быстрого алгоритма обратного квадратного корня и имела меньшую ошибку. [1]

Некоторые недорогие встроенные системы не имеют специализированных инструкций по извлечению квадратного корня. Однако производители этих систем обычно предоставляют тригонометрические и другие математические библиотеки, основанные на таких алгоритмах, как CORDIC .

См. также

[ редактировать ]

Примечания

[ редактировать ]
  1. ^ Использование типа long снижает переносимость этого кода в современных системах. Чтобы код выполнялся правильно, sizeof(long) должно быть 4 байта, в противном случае могут быть получены отрицательные результаты. Во многих современных 64-битных системах sizeof(long) составляет 8 байт. Более портативная замена int32_t.
  2. ^ должно быть в диапазоне для представить в виде нормального числа .
  3. ^ Единственные действительные числа, которые можно представить точно как с плавающей запятой, - это те, для которых является целым числом. Остальные числа можно представить только приблизительно, округлив их до ближайшего точно представимого числа.
  4. ^ Поскольку является положительным, .
  1. ^ Jump up to: Перейти обратно: а б с Раскин, Элан (16 октября 2009 г.). «Квадратный корень времени» . Требуется некоторая сборка . Архивировано из оригинала 08 февраля 2021 г. Проверено 7 мая 2015 г.
  2. ^ фейлипу. «z88dk — это набор инструментов разработки программного обеспечения, предназначенный для компьютеров 8080 и z80» . Гитхаб .
  3. ^ Мунафо, Роберт. «Примечательные свойства конкретных чисел» . mrob.com . Архивировано из оригинала 16 ноября 2018 года.
  4. ^ «реализация sqrt в fdlibm — см. обсуждение W. Kahan и KC Ng в комментариях в нижней половине этого кода» .
  5. ^ Молер, Клив (19 июня 2012 г.). «Симплектическая космическая война» . MATLAB Central — Уголок Клива . МАТЛАБ . Проверено 21 июля 2014 г.
  6. ^ Jump up to: Перейти обратно: а б с Соммефельдт, Рис (19 декабря 2006 г.). «Происхождение Fast InvSqrt() в Quake3 — Часть вторая» . За пределами 3D . Проверено 19 апреля 2008 г.
  7. ^ Jump up to: Перейти обратно: а б с д Соммефельдт, Рис (29 ноября 2006 г.). «Происхождение Fast InvSqrt() в Quake3» . За пределами 3D . Проверено 12 февраля 2009 г.
  8. ^ Блинн 1997 , стр. 80–84.
  9. ^ Пилар, Шейн (1 июня 2021 г.). «Быстрый обратный квадратный корень... в 1997 году?!» .
  10. ^ «Дискуссия на CSDN» . Архивировано из оригинала 2 июля 2015 г.
  11. ^ Ломонт 2003 , с. 1-2.
  12. ^ Зафар, Саад; Адапа, Равитея (январь 2014 г.). «Проектирование аппаратной архитектуры и отображение алгоритма быстрого обратного квадратного корня» . Международная конференция по достижениям в электротехнике (ICAEE) , 2014 г. стр. 1–4. дои : 10.1109/ICAEE.2014.6838433 . ISBN  978-1-4799-3543-7 . S2CID   2005623 .
  13. ^ Миддендорф 2007 , стр. 155–164.
  14. ^ Блинн 2003 , с. 130.
  15. ^ «quake3-1.32b/code/game/q_math.c» . Квейк III Арена . программное обеспечение id . Архивировано из оригинала 29 июля 2017 г. Проверено 21 января 2017 г. {{cite web}}: CS1 maint: bot: исходный статус URL неизвестен ( ссылка )
  16. ^ Эберли 2001 , с. 504.
  17. ^ Ломонт 2003 , с. 1.
  18. ^ Ломонт 2003 .
  19. ^ Ломонт 2003 , с. 3.
  20. ^ МакЭнири 2007 , с. 2, 16.
  21. ^ Jump up to: Перейти обратно: а б Эберли 2001 , с. 2.
  22. ^ Туман, Агнер. «Списки задержек инструкций, пропускной способности и сбоев в микрооперациях для процессоров Intel, AMD и VIA» (PDF) . Проверено 8 сентября 2017 г.
  23. ^ Гольдберг 1991 , с. 7.
  24. ^ Голдберг 1991 , стр. 15–20.
  25. ^ Голдберг 1991 , с. 16.
  26. ^ МакЭнири 2007 , с. 3.
  27. ^ Хеннесси и Паттерсон 1998 , с. 305.
  28. ^ Харди 1908 , с. 323.
  29. ^ МакЭнири 2007 , с. 6.
  30. ^ Jump up to: Перейти обратно: а б Ломонт 2003 , с. 10.
  31. ^ Ломонт 2003 , стр. 10–11.
  32. ^ Мэтью Робертсон (24 апреля 2012 г.). «Краткая история InvSqrt» (PDF) . УНБСЖ.
  33. ^ Кадлец, Ян (2010). «Řrřlog::Улучшение быстрого извлечения квадратного корня» (личный блог). Архивировано из оригинала 9 июля 2018 г. Проверено 14 декабря 2020 г. {{cite web}}: CS1 maint: bot: исходный статус URL неизвестен ( ссылка )
  34. ^ Moroz et al. 2018 .
  35. ^ Мюллер, Жан-Мишель (декабрь 2020 г.). «Элементарные функции и приближенные вычисления» . Труды IEEE . 108 (12): 2146. doi : 10.1109/JPROC.2020.2991885 . ISSN   0018-9219 . S2CID   219047769 .

Библиография

[ редактировать ]

Дальнейшее чтение

[ редактировать ]
[ редактировать ]

Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: eddee18bf2d133f9fdf0bd6bced008b8__1721956440
URL1:https://arc.ask3.ru/arc/aa/ed/b8/eddee18bf2d133f9fdf0bd6bced008b8.html
Заголовок, (Title) документа по адресу, URL1:
Fast inverse square root - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)