по известному алгоритму из Quake и затем делить единицу на эту величину
эта идея мне приходила в голову, там даже проще чем Вы пишите, инверсию корня делать ненужно, лучше его умножить на аргумент, это заметно добавляет точности. Я это всё опробовал, вот рабочий код:
Код:
double fast_sqrt(double x)
{
double y=0;
asm ("movq %1, %%rax; shr $1, %%rax; movq $6910469321099104000, %%rbx; "
"subq %%rax, %%rbx; movq %%rbx, %0;"
:"=r"(y)
:"r"(x)
:"%rax","%rbx"
);
y=y*(3-(x*y*y));
y=y*(12-(x*y*y));
y=y*(768-(x*y*y));
return y*x/8192;
}
даёт точность почти double, если убрать последнюю итерацию - получается почти float, если оставить только одну, то получается 3 верных знака. Но но скорости это хозяйство отстаёт от sqrt. Если ограничиться точностью float и вставить тело функции прямо в код, то работает в 1.5 раза быстрее, что тоже несущественно.
-- 04.05.2024, 18:50 --можно сделать LUT и считать результат линейной интерполяцией
я не знаю что это такое, но делал вот такую штуку, если Вы об этом:
Код:
const uint64_t root[64]=
{ 0,69827414112268,138604612414727,206377602297819,
273189127023833,339078981026126,404084287098361,
468239740956932,531577827751080,594129014356004,
655921920679557,716983472715828,777339039667879,
837012557120638,896026637960214,954402672497414,
1012160919052590,1069320586089555,1125899906842624,
1181916207258746,1237385967972342,1292324880941192,
1346747901294976,1400669294881971,1454102681942250,
1507061077286234,1559556927314419,1611602144176602,
1663208137336207,1714385842776648,1765145750061507,
1815497927438199,1865452045155277,1964202921218288,
2061468567839650,2157314049294893,2251799813685248,
2344982138838073,2436913524311641,2527643037258634,
2617216618617683,2705677355056896,2793065721238116,
2879419796267360,2964775457615668,3049166555311868,
3132625068806179,3215181248566246,3296863744183466,
3377699720527872,3457714963286720,3536933975049166,
3615380062951923,3693075418774505,3770041192264134,
3846297558376920,3921863779041097,3996758259978076,
4070998603056240,4144601654599387,4217583550025434,
4289959755150440,4361745104457490,4432953836598623 };
asm ("movq %1, %%rax;"
"movq %%rax, %%rbx;"
"xorq %%rcx, %%rcx; incq %%rcx; rcr $2, %%rcx;" // знаковый бит
"andq %%rcx, %%rbx; subq %%rbx, %%rax;"
"shr $2, %%rcx; decq %%rcx; movq %%rcx, %%rdx; incq %%rcx;"
"shr $9, %%rcx; addq %%rcx, %%rax;"
"decq %%rcx; notq %%rcx; andq %%rcx, %%rdx; shr $1, %%rax;"
"addq %%rdx, %%rax; addq %%rbx, %%rax;"
"movq %%rax, %%rbx; andq %%rcx, %%rbx;" // порядок
"notq %%rcx; andq %%rcx, %%rax;"
"shr $38, %%rax; xor %%al, %%al; shr $5, %%rax;" // 8-битный адрес мантиссы
"movq %2, %%rcx; add %%rax, %%rcx; xorq (%%rcx), %%rbx;" // из массива
"movq %%rbx, %0;"
:"=r"(br)
:"r"(ar), "r"(root)
:"%rax", "%rbx", "%rcx", "%rdx"
);
tmp=ar/br; br+=tmp; br/=2;
смысл такой - несколько старших бит смещённой мантиссы double используются в качестве адреса массива, содержащего готовые значения корня. Но мне кажется затея так себе, доступ к памяти в данном случае слишком дорогой, это сводит всё на нет.