ПрограммированиеФорумОбщее

Быстрое [s]взятие квадратного корня из целого 64-битного числа[/s] нахождение расстояния между двумя целочисленными точками. (3 стр)

Страницы: 1 2 3 4 5 Следующая »
#30
18:51, 22 апр 2011

SQRTPD вам не поможет?
А если проц новый то вообще вычисли расстояние между жвумя точками дело плёвое
SUBPD -> DPPD -> SQRTSD

#31
19:00, 22 апр 2011

Сразу код на асме можно?

#32
21:46, 22 апр 2011

Код чего? Как найти расстояние меджу двумя точками в 2-д пространстве если их координаты заданны int64 (а может сразу Float64 хранить?)?

#33
22:46, 22 апр 2011

Bishop
> Код чего? Как найти расстояние меджу двумя точками в 2-д пространстве если их
> координаты заданны int64

Нет, если координаты заданы int32 (но корень там придётся брать уже из int64, так как сумма квадратов).
Да, мне нужен асмокод этого с использованием спец.команд. Интересно сравнить как бы.

#34
23:37, 22 апр 2011

Ты мне скажи в чём координаты хранишь изначально.

Сори, понял, щас напишу

#35
0:19, 23 апр 2011

код написан для FPC из Trunk позднее 4го апреля (тогда применили мой патч для поддержки инструкций SSE3+)

//Предпологаю что A и B не выравняны никак и являються 8-ми байтными областями памяти
//(с возможностью ПРОЧИТАТЬ их как 16-ти байтные области не получив ошибку сегментации)
//Т.к. непомню calling conversion x86 пишу под х64:
//[RCX] - адрес А; [RDX] - адрес B; [RAX] - Result (нижние 32 бита)
Function Dist(Const A, B: Int32_Vec2): Int32; Assembler; NoStackFrame;
Asm
 MOVUPS XMM0, [RCX]
 MOVUPS XMM1, [RDX]
 SUBPD XMM0, XMM1
 DPPD XMM0, XMM0, 000110001b
 SQRTSD XMM0, XMM0
 CVTSD2SI EAX, XMM0
End;   

P.S. Тьфу, совсем плохо уже соображаю, вот верный код

//Предпологаю что A и B не выравняны никак и являються 8-ми байтными областями памяти
//Т.к. непомню calling conversion x86 пишу под х64:
//[RCX] - адрес А; [RDX] - адрес B; [RAX] - Result (нижние 32 бита)
Function Dist(Const A, B: Int32_Vec2): Int32; Assembler; NoStackFrame;
Asm
 CVTDQ2PD XMM0, [RCX]
 CVTDQ2PD XMM1, [RDX]
 SUBPD XMM0, XMM1
 DPPD XMM0, XMM0, 000110001b
 SQRTSD XMM0, XMM0
 CVTSD2SI EAX, XMM0
End;   
#36
15:01, 23 апр 2011

Bishop
> //[RCX] - адрес А; [RDX] - адрес B; [RAX] - Result (нижние 32 бита)

Для х86 она такая же, только вместо RAX пишется EAX
А что вообще делает DPPD и что за число 000110001? (равное 49).

#37
17:42, 23 апр 2011

Тебе поможет документация Intel по asm командам.
Это инструкция добавлена в SSE4.1 и осуществляет скалярное произведение. imm8 значение 00110001 говорит что нужно взять скал\рное произедение 2-х компонентного вектора (теоретически можно и однокомпонентного) и записать результат в младшие 64-бита SSE регистра (можно в старшие или и туда и туда)

Еще есть интрукция DPPS она может до 4-х компонентных векторов, т.к. работает с Float32, тогда как DPPD с Float64

#38
16:17, 28 апр 2011

Увы, у меня эта команда не пошла...

Тогда я решил написать такую функцию:

function Dist(dx, dy: integer): integer;  
var
  a, b: integer;
begin
  a := abs(dx);
  b := abs(dy);
  if a>b then begin
    b := b shr 1 - a*(1-sqrt(3)/2); //???
    if b >= 0 then result := a+b else result := a;
  end else begin
    a := a shr 1 - b*(1-sqrt(3)/2); //???
    if a >= 0 then result := a+b else result := b;
  end;
end;         

В метрике, определяемой этой функцией, круги выглядят, как правильные 12-угольники, функция может быть на 3.5% больше истинного результата, и не может быть меньше. Приближение хорошее.
Но есть проблема.
Вот это вот a*(1-sqrt(3)/2) мне не нравится.
Оптимальный вариант - это умножение на 575416509 (это (1-sqrt(3)/2)*2^32), и взятие старших 32 битов длинного результата (сделать imul и использовать edx из пары edx:eax, в которую пишется результат умножения).
К сожалению, в Д7 нету никакого оператора для этого простого действия.
Пришлось делать асмотрах.
Как без еретичного асма сделать это? Вариант (a*8780) shr 16 годится только для не очень больших чисел, а у меня большие бывают.

#39
16:25, 28 апр 2011

TarasB
если устраивает такая точность можно сделать таблицей из 64 или 256 (2^(2n)) значений

#40
16:37, 28 апр 2011

Aslan
> если устраивает такая точность можно сделать таблицей из 64 или 256 (2^(2n))
> значений

И как я к таблице буду обращаться? Таблица-то нужна с шагом, пропорциональным значению.
То есть не 2,4,6,8,10,12,14, ..., а 1,2,5,10,20,50,100...

#41
16:42, 28 апр 2011

Кому интересно, вот рабочий асмотрах-вариант:

function Dist(dx, dy: integer): integer;   
asm
  push ebx
  mov ecx, edx    // ecx = dy
  cdq
  xor eax, edx  
  mov ebx, eax
  sub ebx, edx    // ebx = abs(dx)
  mov eax, ecx    // ebx = abs(dx), eax = dy
  cdq
  xor eax, edx
  sub eax, edx    // ebx = abs(dx), eax = abs(dy)
  cmp eax, ebx
  jl @1
  // eax>ebx
  mov ecx, eax    // ecx = abs(dy)
  mov edx, 575416509
  imul edx         // edx = abs(dy) * (1-sqrt(3)/2)
  shr ebx, 1
  mov eax, ecx
  sub ebx, edx
  jl @2
  add eax, ebx
  @2:
  pop ebx
  ret
  @1:
  // ebx>eax
  mov ecx, eax
  mov eax, ebx    // eax,ebx = abs(dx), ecx = abs(dy)
  mov edx, 575416509
  imul edx         // edx = abs(dx) * (1-sqrt(3)/2)
  shr ecx, 1
  mov eax, ebx
  sub ecx, edx    // ecx = abs(dy)/2 - abs(dx)*(1-sqrt(3)/2)
  jl @3
  add eax, ecx
  @3:
  pop ebx
end;
#42
16:44, 28 апр 2011

Bishop
> Как найти расстояние меджу двумя точками в 2-д пространстве
Если тебя устраивает 3.5% погрешность, то устроят и Кармаковские 6% (1.5*100/sqrt(2)-100):

int P_AproxDistance(int dx, int dy)
{
  dx = abs(dx);
  dy = abs(dy);
  if (dx < dy)
    return dx+dy-(dx>>1);
  return dx+dy-(dy>>1);
}
#43
17:19, 28 апр 2011

entryway
Этот вариант был в 27 посте (я его со времён 27 поста как раз до такого вида и дооптимизировал)...
Только нафига dx+dy-(dy>>1) вместо dx+dy>>1?
Выдаёт 8-угольник, для него расхождение не 6, а 12 процентов. Нет, он мне не годится.
А 12-угольник на 25% всего медленнее.

#44
17:55, 28 апр 2011

TarasB
int64 разве нет в твоем делфи чтобы сделать (a*8780) shr 16? Ну и разрядов еще парочку сократить можно.

Такое еще загуглил первой ссылкой:
http://www.flipcode.com/archives/Fast_Approximate_Distance_Functions.shtml

Страницы: 1 2 3 4 5 Следующая »
ПрограммированиеФорумОбщее

Тема в архиве.