Для моего кода BigInteger вывод оказался медленным для очень больших BigInteger. Итак, теперь я использую рекурсивный алгоритм «разделяй и властвуй», которому по-прежнему требуется 2'30 дюймов, чтобы преобразовать самое большое известное в настоящее время простое число в десятичную строку из более чем 22 миллионов цифр (но всего 135 мс, чтобы превратить ее в шестнадцатеричную строку) .
Я все еще хочу сократить время, поэтому мне нужна процедура, которая может очень быстро делить NativeUInt (т.е. UInt32 на 32-битных платформах, UInt64 на 64-битных платформах). Поэтому я использую умножение на константу. Это отлично работает в 32-битном коде, но я не уверен на 100% для 64-битного.
Итак, мой вопрос: есть ли способ проверить достоверность результатов умножения на константу для беззнаковых 64-битных значений? Я проверил 32-битные значения, просто попробовав все значения UInt32 (0..$FFFFFFFF). Это заняло ок. 3 минуты. Проверка всех UInt64 заняла бы гораздо больше времени, чем моя жизнь. Есть ли способ проверить, надежны ли используемые параметры (постоянные, постсдвиговые)?
Я заметил, что DivMod100()
всегда терпел неудачу для значения, подобного $4000004B
, если выбранные параметры были неправильными (но близкими). Существуют ли специальные значения или диапазоны для проверки 64-битной версии, чтобы мне не нужно было проверять все значения?
Мой текущий код:
const
{$IF DEFINED(WIN32)}
// Checked
Div100Const = UInt32(UInt64($1FFFFFFFFF) div 100 + 1);
Div100PostShift = 5;
{$ELSEIF DEFINED(WIN64)}
// Unchecked!!
Div100Const = $A3D70A3D70A3D71;
// UInt64(UInt128($3 FFFF FFFF FFFF FFFF) div 100 + 1);
// UInt128 is fictive type.
Div100PostShift = 2;
{$IFEND}
// Calculates X div 100 using multiplication by a constant, taking the
// high part of the 64 bit (or 128 bit) result and shifting
// right. The remainder is calculated as X - quotient * 100;
// This was tested to work safely and quickly for all values of UInt32.
function DivMod100(var X: NativeUInt): NativeUInt;
{$IFDEF WIN32}
asm
// EAX = address of X, X is UInt32 here.
PUSH EBX
MOV EDX,Div100Const
MOV ECX,EAX
MOV EAX,[ECX]
MOV EBX,EAX
MUL EDX
SHR EDX,Div100PostShift
MOV [ECX],EDX // Quotient
// Slightly faster than MUL
LEA EDX,[EDX + 4*EDX] // EDX := EDX * 5;
LEA EDX,[EDX + 4*EDX] // EDX := EDX * 5;
SHL EDX,2 // EDX := EDX * 4; 5*5*4 = 100.
MOV EAX,EBX
SUB EAX,EDX // Remainder
POP EBX
end;
{$ELSE WIN64}
asm
.NOFRAME
// RCX is address of X, X is UInt64 here.
MOV RAX,[RCX]
MOV R8,RAX
XOR RDX,RDX
MOV R9,Div100Const
MUL R9
SHR RDX,Div100PostShift
MOV [RCX],RDX // Quotient
// Faster than LEA and SHL
MOV RAX,RDX
MOV R9D,100
MUL R9
SUB R8,RAX
MOV RAX,R8 // Remainder
end;
{$ENDIF WIN32}
$1C0000000000000000 div 100 + 1
с пост-сдвигом 6, но результат неn div 100
в старшей части. libdivide дает ожидаемые результаты для 32-битной системы, но, возможно, я не понимаю, как она используется для 64-битной версии. Поэкспериментирую еще немного. - person Rudy Velthuis   schedule 30.01.2016