.code
align 4
Real4_2_ASCII proc Real4string:DWORD,floatnumber:REAL4
mov ecx,Real4string ; The string to fill with the floating point value in ASCII format
mov eax,floatnumber ; Load the floating point value
test eax,eax
je message_PosZero ; check if it is 0.0
cmp eax,080000000h
je message_NegZero ; Check if it is -0.0
; check floating-point exceptions
cmp eax,07F7FFFFFh
ja TestExceptions ; jump to check floating point exceptions
ProcessReal4:
mov byte ptr [ecx],020h ; Write " " to the string
test eax,80000000h ; Check the sign bit
jz No_SignBit
mov byte ptr [ecx],02dh ; write "-" character to the string
and floatnumber,7FFFFFFFh ; Make it an absolute value
and eax,7FFFFFFFh ; Remove the sign bit
No_SignBit:
; Fast Log10(x)-1 routine to calculate the number of digits
shr eax,23 ; Get the 8bit exponent
sub eax,127 ; Adjust for the exponent bias
cvtsi2ss xmm0,eax ; Convert int32 to real4
mulss xmm0,Log10_2 ; Approximate Log10(x) == Log10(2) * exponent bits == 0.30102999566398119 * exponent bits
addss xmm0,PowersOfTen[37*4] ; Add one to get the approximated number of digits from the floating point value
cvtss2si eax,xmm0 ; Convert real4 to int32
mov ecx,eax ; Save approximated number of digits
mov edx,38+1 ; Highest possible number of digits + 1
add eax,edx ; Get the Power Of Ten offset for the digits rounding check
; Now do the check to get the exact rounded number of digits from the floating point value
; We can do this by comparing it to the closest Power Of Ten below the floating point value
movss xmm0,floatnumber
comiss xmm0,PowersOfTen[eax*4-4]
jc ExactLog10xMin1 ; Is it below the closest Power Of Ten?
cmp ecx,edx ; It is above, also check the approximated number of digits
je ExactLog10xMin1 ; Is it not above the highest possible number of digits skip adjustment
dec edx ; Adjust the number of digits by subtracting one
ExactLog10xMin1: ; Now we are allmost done to get the exact number of digits
; There is one exception, the lowest Power Of Ten check value is out of range ( 1.0E+39 )
; See the last added value in the PowersOfTen table, it's used for the out of range check
sub edx,ecx ; edx holds the offsets for the PowersOfTen table and the scientific notation string table
mulss xmm0,PowersOfTen[edx*4] ; Get the calculated Power Of Ten value and multiply it with the floating point value
comiss xmm0,PowersOfTen[38*4] ; Compare to 10.0
jnc ExactNumDigits ; Is it below 10.0?
inc edx ; It is below, adjust the offset for the scientific notation string
mulss xmm0,PowersOfTen[38*4] ; Adjust decimal position ( it also solves the out of range issue )
ExactNumDigits: ; At this point we have the exact number of digits from the floating point value
mulss xmm0,PowersOfTen[42*4] ; Get the 7 significant digits from the range -1.175494E-38 to 3.402823E+38
cvtss2si eax,xmm0 ; We want a Natural Number
cvtsi2ss xmm0,eax ; So, remove the digits after the decimal point
shufps xmm0,xmm0,0 ; Splat...., make 4 copies from the real4 number
movaps xmm1,xmm0 ; Copy to a total of 8 copies
mulps xmm0,dividers ; Produce base10 numbers
mulps xmm1,dividers+16 ; Produce base10 numbers
movaps xmm2,xmm0 ; Copy them
movaps xmm3,xmm1 ; Copy them
mulps xmm2,div10 ; Nullify least significant base10 numbers
mulps xmm3,div10 ; Nullify least significant base10 numbers
cvttps2dq xmm0,xmm0 ; Truncate remaining fractions
cvttps2dq xmm1,xmm1 ; Truncate remaining fractions
cvtdq2ps xmm0,xmm0 ; Convert back to real4
cvtdq2ps xmm1,xmm1 ; Convert back to real4
cvttps2dq xmm2,xmm2 ; Truncate remaining fractions
cvttps2dq xmm3,xmm3 ; Truncate remaining fractions
cvtdq2ps xmm2,xmm2 ; Convert back to real4
cvtdq2ps xmm3,xmm3 ; Convert back to real4
mulps xmm2,mul10 ; Move them back in the correct base10 position
mulps xmm3,mul10 ; Move them back in the correct base10 position
subps xmm0,xmm2 ; Subtract to get the extracted digits
subps xmm1,xmm3 ; Subtract to get the extracted digits
cvttps2dq xmm0,xmm0 ; Convert back to int32
cvttps2dq xmm1,xmm1 ; Convert back to int32
shufps xmm0,xmm0,11100001b ; Swap the 2 first digits, the first digit is always zero
; Now we can write the decimal point for free ( no more memory swaps )
; Using a prepared ASCIIconverter constant
packssdw xmm0,xmm1 ; Pack 8 x 32bit to 8 x 16bit ( signed but, we are within the limit )
packuswb xmm0,xmm0 ; Pack 8 x 16bit to 16 x 8bit unsigned
movq xmm1,ASCIIconverterE ; Prepared to insert a decimal point, convert to ASCII and terminate string in one go
paddb xmm1,xmm0 ; Convert the number to ASCII
mov edx,Scientific_sz[edx*4]; Get the 4 byte scientific notation string
mov ecx,Real4string
movq qword ptr [ecx+1],xmm1 ; Write the 7 significant digits
mov [ecx+9],edx ; Write the scientific notation string
ret ; Done....
TestExceptions:
cmp eax,07F800000h
je message_Inf
cmp eax,07F800001h
je message_SNaN
cmp eax,07FBFFFFFh
je message_SNaN
cmp eax,07FC00000h
je message_QNaN
cmp eax,07FFFFFFFh
je message_QNaN
cmp eax,0FFC00001h
je message_QnegNaN
cmp eax,0FFBFFFFFh
je message_SnegNaN
cmp eax,0FF800001h
je message_SnegNaN
cmp eax,0FFC00000h
je message_Indeterm
cmp eax,0FF800000h
je message_NegInf
cmp eax,0FFFFFFFFh
je message_QnegNaN
jmp ProcessReal4 ; No exceptions found, proceed...
message_QnegNaN:
movaps xmm0,oword ptr szQnegNaN
movaps oword ptr [ecx],xmm0
ret
message_SnegNaN:
movaps xmm0,oword ptr szSnegNaN
movaps oword ptr [ecx],xmm0
ret
message_Indeterm:
movaps xmm0,oword ptr szIndeterm
movaps oword ptr [ecx],xmm0
ret
message_NegInf:
movaps xmm0,oword ptr szNegInf
movaps oword ptr [ecx],xmm0
ret
message_Inf:
movaps xmm0,oword ptr szInf
movaps oword ptr [ecx],xmm0
ret
message_SNaN:
movaps xmm0,oword ptr szSNaN
movaps oword ptr [ecx],xmm0
ret
message_QNaN:
movaps xmm0,oword ptr szQNaN
movaps oword ptr [ecx],xmm0
ret
message_PosZero:
movaps xmm0,oword ptr szPosZero
movaps oword ptr [ecx],xmm0
ret
message_NegZero:
movaps xmm0,oword ptr szNegZero
movaps oword ptr [ecx],xmm0
ret
Real4_2_ASCII endp