Я полагаюсь на старую реализацию, которая делает некоторые вычисления и конвертирует float
к int
,
Однако после повторения расчетов некоторые значения отключаются из-за разных результатов округления.
Это сводится к тому, что двоичный файл использует следующий код для преобразования float
в int
(long
).
lea ecx, [esp+var_8] ; Load Effective Address
sub esp, 10h ; Integer Subtraction
and ecx, 0FFFFFFF8h ; Logical AND
fld st ; Load Real
fistp qword ptr [ecx] ; Store Integer and Pop
fild qword ptr [ecx] ; Load Integer
mov edx, [ecx+4]
mov eax, [ecx]
test eax, eax ; Logical Compare
jz short loc_3 ; Jump if Zero (ZF=1)
loc_1:
fsubp st(1), st ; Subtract Real and Pop
test edx, edx ; Logical Compare
jz short loc_2 ; Jump if Zero (ZF=1)
fstp dword ptr [ecx] ; Store Real and Pop
mov ecx, [ecx]
add esp, 10h ; Add
xor ecx, 80000000h ; Logical Exclusive OR
add ecx, 7FFFFFFFh ; Add
adc eax, 0 ; Add with Carry
retn ; Return Near from Procedure
; ---------------------------------------------------------------------------
loc_2:
fstp dword ptr [ecx] ; Store Real and Pop
mov ecx, [ecx]
add esp, 10h ; Add
add ecx, 7FFFFFFFh ; Add
sbb eax, 0 ; Integer Subtraction with Borrow
retn ; Return Near from Procedure
; ---------------------------------------------------------------------------
loc_3:
test edx, 7FFFFFFFh ; Logical Compare
jnz short loc_1 ; Jump if Not Zero (ZF=0)
fstp dword ptr [ecx] ; Store Real and Pop
fstp dword ptr [ecx] ; Store Real and Pop
add esp, 10h ; Add
retn ; Return Near from Procedure
Это ведет себя иначе, чем просто делает без знака int var = (unsigned int)floatVal;
,
Я считаю, что это старый ftol
реализация и было сделано, потому что преобразование из float
в int
был очень медленным, и компилятору нужно было изменить режим округления FPU.
Это выглядит очень похоже на этот http://www.libsdl.org/release/SDL-1.2.15/src/stdlib/SDL_stdlib.c
Может кто-нибудь помочь мне в преобразовании функции в C? Или скажите мне, как я могу создать встроенную функцию ASM с float
параметр и возврат int
используя Visual Studio. Тот в SDL_sdtlib.c
не имеет заголовка, и я не уверен, как вызвать его без аргументов функции.
Это не совсем отвечает на заданные вопросы, но я хотел начать с более тщательного перевода на английский. Это не идеально, и есть несколько строк, где я все еще пытаюсь отследить намерение. Всем пожалуйста высказывайтесь с вопросами и исправлениями.
lea ecx, [esp+var_8]; Load Effective Address // make ecx point to somewhere on the stack (I don't know where var_8 is being generated in this case, but I'm guessing it's set such that it makes ecx point to the local stack space allocated on the next line)
sub esp, 10h ; Integer Subtraction // make room on stack for 16 bytes of local variable -- doesn't all get used but adds padding to allow aligned loads and stores
and ecx, 0FFFFFFF8h ; Logical AND // align pointer in ecx to 8-byte boundary
fld st ; Load Real // duplicates whatever was last left (passed by calling convention) on the top of the FPU stack -- st(1) = st(0)
fistp qword ptr [ecx] ; Store Integer and Pop // convert st(0) to *64bit* int (truncate), store in aligned 8 bytes (of local variable space?) pointed to by ecx, and pop off the top value from the FPU stack
fild qword ptr [ecx] ; Load Integer // convert truncated value back to float and leave it sitting on the top of the FPU stack
;// at this point:
;// - st(0) is the truncated float
;// - st(1) is still the original float.
;// - There is a 64bit integer representation pointed to by [ecx]
mov edx, [ecx+4] ; // move [bytes 4 thru 7 of integer output] to edx (most significant bytes)
mov eax, [ecx] ; // move [bytes 0 thru 3 of integer output] to eax (least significant bytes) -- makes sense, as EAX should hold integer return value in x86 calling conventions
test eax, eax ; Logical Compare // (http://stackoverflow.com/questions/13064809/the-point-of-test-eax-eax)
jz short loc_3 ; Jump if Zero (ZF=1) // if the least significant 4 bytes are zero, goto loc_3
; // else fall through to loc_1
loc_1: ;
fsubp st(1), st ; Subtract Real and Pop // subtract the truncated float from the original, store in st(1), then pop. (i.e. for 1.25 st(0) ends up 0.25, and the original float is no longer on the FPU stack)
test edx, edx ; Logical Compare // same trick as earlier, but for the most significant bytes now
jz short loc_2 ; Jump if Zero (ZF=1) // if the most significant 4 bytes from before were all zero, goto loc_2 -- (i.e. input float does not overflow a 32 bit int)
fstp dword ptr [ecx] ; Store Real and Pop // else, dump the fractional portion of the original float over the least significant bytes of the 64bit integer
;// at this point:
;// - the FPU stack should be empty
;// - eax holds a copy of the least significant 4 bytes of the 64bit integer (return value)
;// - edx holds a copy of the most significant 4 bytes of the 64bit integer
;// - [ecx] points to a float representing the part of the input that would be lost in integer truncation
;// - [ecx+4] points at the most significant 4 bytes of our 64bit integer output (probably considered garbage now and not used again)
mov ecx, [ecx] ; // make ecx store what it's pointing at directly instead of the pointer to it
add esp, 10h ; Add // clean up stack space from the beginning
xor ecx, 80000000h ; Logical Exclusive OR // mask off the sign bit of the fractional float
add ecx, 7FFFFFFFh ; Add // add signed int max (still need to figure out why this)
adc eax, 0 ; Add with Carry // clear carry bit
retn ; Return Near from Procedure
; ---------------------------------------------------------------------------
loc_2:
;// at this point: the FPU stack still holds the fractional (non-integer) portion of the original float that woud have been lost to truncation
fstp dword ptr [ecx] ; Store Real and Pop // store non-integer part as float in local stack space, and remove it from the FPU stack
mov ecx, [ecx] ; // make ecx store what it's pointing at directly instead of the pointer to it
add esp, 10h ; Add // clean up stack space from the beginning
add ecx, 7FFFFFFFh ; Add // add signed int max to the float we just stored (still need to figure out why this)
sbb eax, 0 ; Integer Subtraction with Borrow // clear carry bit
retn ; Return Near from Procedure
; ---------------------------------------------------------------------------
loc_3:
test edx, 7FFFFFFFh ; Logical Compare // test the most significant bytes for signed int max
jnz short loc_1 ; Jump if Not Zero (ZF=0) // if the high bytes equal signed int max go back to loc_1
fstp dword ptr [ecx] ; Store Real and Pop // else, empty the FPU stack
fstp dword ptr [ecx] ; Store Real and Pop // empty the FPU stack
add esp, 10h ; Add // clean up stack space from the beginning
retn ; Return Near from Procedure
Других решений пока нет …