flat assembler
Message board for the users of flat assembler.
![]() Goto page Previous 1, 2, 3, 4, 5 Next |
Author |
|
Reverend 22 Dec 2005, 19:56
Hello all
I haven't been on board for some time. Sorry guys, I promised to create the conversion procedures and was unreachable. I really had lack of time ![]() But I have written it. Don't worry, routines are ready and heavily tested. ITOA Code: ;=============================================================================== ; integer to ASCII conversion procedure ; ; input: ; number - number to convert FTOA Code: ;=============================================================================== ; float to ASCII conversion procedure ; ; input: ; buffer - pointer to memory where output will be saved ; precision - number of digits after dot ; ; output: ; no immediate output ; ; notes: ; separate integer and mantisa part with dot '.' ; so GOOD 123.456 ; WRONG 123,456 ; ; coded by Reverend // HTB + RAG ;=============================================================================== proc ftoa buffer, precision locals status_original dw ? status_changed dw ? integer dd ? mantisa dd ? signed dd ? endl push eax edi ecx ; -------------------------------- ; set correct precision mov eax, [precision] cmp eax, 51 jb @F mov eax, 51 @@: mov [precision], eax ; -------------------------------- ; change control wortd of fpu to prevent rounding fnstcw [status_original] mov ax, [status_original] or ax, 0000110000000000b mov [status_changed], ax fldcw [status_changed] ; -------------------------------- ; check if signed xor eax, eax fst [signed] test [signed], 80000000h setnz al mov [signed], eax ; -------------------------------- ; cut integer and mantisa separately fld st0 fld st0 ; st0 = x, st1 = x frndint fist [integer] ; st0 = x, st1 = x fabs fsubp st1, st0 ; st0 = mantisa(x) ; -------------------------------- ; save integer part in buffer mov edi, [buffer] push [signed] push edi push 10 push [integer] call itoa add edi, eax mov al, '.' stosb ; -------------------------------- ; save mantisa part in buffer mov ecx, [precision] dec ecx .loop: fimul [ftoa.ten] fld st0 frndint fist [mantisa] fsubp st1, st0 push 0 push edi push 10 push [mantisa] call itoa add edi, eax ftst fnstsw ax test ax, 0100000000000000b jz @F test ax, 0000010100000000b jz .finish @@: loop .loop fldcw [status_original] fimul [ftoa.ten] fist [mantisa] push 0 push edi push 10 push [mantisa] call itoa ; -------------------------------- ; restore previous values .finish: fstp st0 stc pop ecx edi eax ret endp idata ftoa.ten dd 10 endg ATOI Code: ;=============================================================================== ; ASCII to integer conversion procedure ; ; input: ; string - pointer to string ; radix - base system like 10 for decimal, 16 for hexadecimal, etc. ; ; output: ; eax - number ; ; possible errors: ; radix exceeds 16 ; number is bigger than dword can store ; digit is not 0...9 or A...F ; ; if error occurs the value of eax is indefinable ATOF Code: ;=============================================================================== ; ASCII to float conversion procedure ; ; input: ; string - pointer to string ; ; output: ; st0 - number changed into float ; ; coded by Reverend // HTB + RAG ;=============================================================================== proc atof string push eax ecx esi fninit fild [atof.ten] fldz fldz mov esi, [string] cmp byte [esi], '-' jnz @F inc esi @@: xor eax, eax align 4 .loop.integer_part: lodsb cmp al, '.' jz .mantisa test al, al jz .exit fmul st0, st2 sub al, '0' cmp al, 9 jbe @F sub al, 'A' - '9' - 1 @@: push eax fiadd dword [esp] add esp, 4 jmp .loop.integer_part .mantisa: xor ecx, ecx @@: inc ecx lodsb test al, al jnz @B cmp ecx, 10 ; max 10 digits in mantisa jbe @F mov ecx, 10 @@: std dec esi fxch st1 align 4 .loop.mantisa_part: lodsb cmp al, '.' jz .exit sub al, '0' cmp al, 9 jbe @F sub al, 'A' - '9' - 1 @@: push eax fiadd dword [esp] add esp, 4 fdiv st0, st2 jmp .loop.mantisa_part .exit: cld faddp st1, st0 ffree st1 mov eax, [string] cmp byte [eax], '-' jnz @F fchs @@: stc ; always returns no error pop esi ecx eax ret endp idata atof.ten dd 10 endg
|
|||||||||||
![]() |
|
Garthower 08 Feb 2007, 21:35
Excellent source code Reverend! Now I really understand how to convert float number to string. Thanks!
|
|||
![]() |
|
Vasilev Vjacheslav 09 Feb 2007, 10:29
sample 'test_ftoa.exe' have a bug? it crashed in itoa function (at 401128h)
|
|||
![]() |
|
vid 09 Feb 2007, 13:52
Grthower: it cannot be 100% accurate as long as it uses FPU code.
But in this case, it is problem of FP numbers as such. There is no binary FP number representation for exactly 3.1415926535, so FASM (not that procedure) translated it to nearest possible number. |
|||
![]() |
|
Vasilev Vjacheslav 09 Feb 2007, 15:32
maybe setting FPU control word may help with proper rounding?
|
|||
![]() |
|
Reverend 09 Feb 2007, 19:45
Dealing with control word won't change much as it only says to round up, down or to cut mantissa
![]() Now I know a bit more how are float values stored (from my university classes) and so maybe I'll create another non-FPU version of FTOA. |
|||
![]() |
|
Reverend 09 Feb 2007, 22:18
This topic hooked me, and I wrote today a version that reads the float number directly as the IEEE 754 standard says. Below is the code that reads single precision only (other precision can be made by analogy):
Code: ;=============================================================================== ; routine converting float to ascii value proc ftoa value_ptr, string_ptr locals status_original dw ? status_changed dw ? endl mov eax, [value_ptr] mov ecx, [string_ptr] mov eax, [eax] ;-------check-sign-------------------------------------------------------------- xor edx, edx test eax, 80000000h jnz .minus .plus: mov byte [ecx], '+' inc ecx jmp .sign_ready .minus: mov byte [ecx], '-' inc ecx ;-------get-exponent-value------------------------------------------------------ .sign_ready: mov dword [ecx], '1 * ' add ecx, 4 mov word [ecx], '2^' add ecx, 2 ; e = Exp -127 mov edx, eax and edx, 7FFFFFFFh shr edx, 23 sub edx, 7Fh jns .not_signed mov byte [ecx], '(' inc ecx .not_signed: push eax ecx stdcall itoa, edx, 10, ecx, TRUE mov edx, eax pop ecx eax cmp byte [ecx - 1], '(' jnz .not_signed2 add ecx, edx mov byte [ecx], ')' inc ecx jmp .exponent_ready .not_signed2: add ecx, edx ;-------get-mantissa-value------------------------------------------------------ .exponent_ready: push ebx edi esi mov edx, eax shl edx, 32 - 23 jz .mantissa_ready ; st0 - numerator - l ; st1 - denominator - m ; st2 - actual denominator - am mov eax, 23 + 1 fninit fld1 fadd st, st ; 0-2 fld1 ; 0-1 1-2 fld1 ; 0-1 1-1 2-2 fldz ; 0-0 1-1 2-1 3-2 .mantissa_loop: dec eax jz .mantissa_loop_finish fxch st2 fmul st, st3 fxch st2 ; 0-l 1-m 2-am 3-2 shl edx, 1 jnc .mantissa_loop fmul st, st2 ; 0-l*am 1-m 2-am 3-2 fadd st, st1 ; 0-(l*am)+m 1-m 2-am 3-2 fxch st1 fmul st, st2 fxch st1 ; 0-(l*am)+m 1-m*am 2-am 3-2 .parity_check: fld st ; 0-(l*am)+m 1-(l*am)+m 2-m*am 3-am 4-2 fdiv st, st4 ; 0-((l*am)+m)/2 1-(l*am)+m 2-m*am 3-am 4-2 ; check if it's divisable by 2 fld st frndint fsubp st1, st fadd st, st push ebx fistp dword [esp] pop ebx test ebx, ebx jnz .mantissa_loop ; if it's divisable, divide both numerator and denominator by 2 fdiv st, st3 fxch st1 fdiv st, st3 fxch st1 jmp .parity_check .mantissa_loop_finish: mov dword [ecx], ' * 1' add ecx, 4 mov byte [ecx], '.' inc ecx ; exchange 2 by 10 fld1 fadd st, st fadd st, st fadd st, st fld1 fadd st, st faddp st1, st fxch st4 fstp st ; 0-l 1-m 2-am 3-10 ; cut mantissa (no rounding) fnstcw [status_original] mov ax, [status_original] or ax, 0000110000000000b mov [status_changed], ax fldcw [status_changed] ; algorithm of integer division .mantissa_output_loop: fmul st, st3 ; 0-10*l 1-m 2-am 3-10 fld st ; 0-10*l 1-10*l 2-m 3-am 4-10 fdiv st, st2 ; 0-(10*l)/m 1-10*l 2-m 3-am 4-10 push eax frndint fist dword [esp] pop eax mov al, [itoa.digits + eax] mov byte [ecx], al inc ecx fmul st, st2 ; 0-[10*l] 1-10*l 2-m 3-am 4-10 fsubp st1, st ; 0-(10*l mod m) 1-m 2-am 3-10 ftst fnstsw ax test ax, 4000h jz .mantissa_output_loop fldcw [status_original] .mantissa_ready: pop esi edi ebx ret endp ;=============================================================================== ; integer to ASCII conversion procedure ; ; input: ; number - number to convert Yes, new ftoa is very, very long. It outputs data in format: s * 2^e * m, where: s - sign, e - exponent, m - mantissa. Code uses FPU heavily, but only for integer operations. There were really huge numbers and they just didn't fit in 32-bit register. But as I do only integer operations, no precision is lost, and thus the output is as exact as it can be. Examples: PI is saved as Code: +1 * 2^1 * 1.57079637050628662109375 |
|||
![]() |
|
Vasilev Vjacheslav 10 Feb 2007, 09:31
for proper rounding in ftoa maybe better to use
110000111111b instead of: 110000000000b ? |
|||
![]() |
|
Vasilev Vjacheslav 10 Feb 2007, 09:40
if someone wants i can post my version of 'double to ascii' algo
ps. also i can post assembler equivalent of visual basic mid(), left() and right() functions, imho very useful for working with strings |
|||
![]() |
|
Mac2004 10 Feb 2007, 10:53
Quote: if someone wants i can post my version of 'double to ascii' algo That would be nice from you... ![]() |
|||
![]() |
|
Vasilev Vjacheslav 10 Feb 2007, 13:28
ok, it is a mid(), left(), right() implementation in assembler
ps. a bit later i put dtoa
_________________ [not enough memory] |
|||||||||||
![]() |
|
dead_body 10 Feb 2007, 18:22
what happened to some files in fasmlib?
where the directory "linux"? |
|||
![]() |
|
vid 11 Feb 2007, 10:19
this thread is pretty old, don't mind it/
|
|||
![]() |
|
Vasilev Vjacheslav 11 Feb 2007, 14:51
here is my version of ftoa/dtoa (pretty long)
_________________ [not enough memory] |
|||||||||||
![]() |
|
Dopefish 03 Jan 2008, 17:41
Reverend wrote: Hello all The ATOF routine seems to output different numbers based on how many are input. I do some calculations based on the number stored in st0. Here are some examples. The left number is the input (atof), and the second number is what ends up in st0. Code: INPUT OUTPUT 1 1.0 1. 21.1000000000 1.0 3.00999999910 1.00 1.20099999910 1.000 1.0201000000 1.0000 1.00200999910 1.00000 1.0002010000 1.000000 1.0000201000 1.0000000 1.00000200910 1.00000000 1.0000002010 1.000000000 1.0000000201 1.0000000000 1.0000000020 1.00000000000 1.0000000002 1.000000000000 1.0000000000 |
|||
![]() |
|
vid 03 Jan 2008, 19:19
i have finished pretty good working atof, with lot of settings, and VERY good precision (because it doesn't use FPU - that also removes lot of problems).
It will be in FASMLIB 0.9.0, or I can post it in advance, if someone's interested... |
|||
![]() |
|
Dopefish 08 Jan 2008, 15:55
I'm interested.
![]() I'm always looking for a good itoa, atoi, ftoa or atof. |
|||
![]() |
|
vid 08 Jan 2008, 16:12
this one is very precise, and thus also very slow
![]() here is ugly copy-pastery of functions involved: Code: ;; @name conv._qfp_add ;; @desc ;; This is internal procedure - do not call it directly. ;; Adds two quad-precision floating point numbers. ;; @arg dest ;; Pointer to buffer for destination number. ;; Can be any of qfpnum1, qfpnum2. ;; @arg qfpnum1 ;; Pointer to first quad precision floating point number. ;; @arg qfpnum2 ;; Pointer to second quad precision floating point number. ;; @ret ;; CF set on error, otherwise ;; EAX = dest ;; @err ERR_OUT_OF_RANGE ;; Resulting number is too large (above or equal binary 1e2147483648) ;; @err ERR_INTERNAL ;; Bug in FASMLIB. Please report. ;; @note Quad-precision floating point number consists of 96bit mantissa ;; (3 dwords, low order dword first). Follows 32bit (dword) signed exponent, ;; that holds position of low order bit of mantissa. ;; There is no sign. Number doesn't need to be normalized. proc conv._qfp_add dest, qfpnum1, qfpnum2 local temp rd 4 push ebx ecx edx esi edi ;esi = qfpnum1, edx = qfpnum2 mov esi, [qfpnum1] mov edx, [qfpnum2] ;EDI = temp lea edi, [temp] ;ebx = position of highest bit in qfpnum1 mov ecx, 2*32 bsr eax, dword [esi+8] jnz @f sub ecx, 32 bsr eax, dword [esi+4] jnz @f sub ecx, 32 bsr eax, dword [esi] jz .qfp1_zero @@: add eax, ecx add eax, dword [esi+12] mov ebx, eax ;EAX = position of highest bit in qfpnum2 mov ecx, 2*32 bsr eax, dword [edx+8] jnz @f sub ecx, 32 bsr eax, dword [edx+4] jnz @f sub ecx, 32 bsr eax, dword [edx] jz .qfp2_zero @@: add eax, ecx add eax, dword [edx+12] ;EBX position of highest bit total cmp eax, ebx jbe @f mov ebx, eax @@: ;set destination mantissa to zero mov dword [edi], 0 mov dword [edi+4], 0 mov dword [edi+8], 0 ;ECX = destination exponent = highbit - 3*32 + 1 lea ecx, [ebx - 3*32 + 1] mov dword [edi + 12], ecx ;add first dword of mantissa libcall conv._qfp_dword, esi, ecx mov dword [edi], eax libcall conv._qfp_dword, edx, ecx add dword [edi], eax adc dword [edi+4], 0 add ecx, 32 ;add second dword of mantissa libcall conv._qfp_dword, esi, ecx add dword [edi+4], eax libcall conv._qfp_dword, edx, ecx add dword [edi+4], eax adc dword [edi+8], 0 add ecx, 32 ;add third dword of mantissa libcall conv._qfp_dword, esi, ecx add dword [edi+8], eax libcall conv._qfp_dword, edx, ecx add dword [edi+8], eax ;if we don't need to add another bit, we're done jnc .done ;insert top bit rcr dword [edi+8], 1 rcr dword [edi+4], 1 rcr dword [edi], 1 add dword [edi+12], 1 jo .error_overflow jmp .done ;QFP2 is zero, return QFP1 .qfp2_zero: mov edi, esi jmp .done ;QFP1 is zero, return QFP2 .qfp1_zero: mov edi, edx jmp .done .done: ;copy result from temp (pointed by EDI) to dest mov esi, [dest] mov eax, [edi] mov [esi], eax mov eax, [edi+4] mov [esi+4], eax mov eax, [edi+8] mov [esi+8], eax mov eax, [edi+12] mov [esi+12], eax ;return dest mov eax, esi .rnc: clc .r: pop edi esi edx ecx ebx ret .rc: stc jmp .r .error_overflow: mov eax, ERR_OUT_OF_RANGE jmp .rc .error_internal: mov eax, ERR_FASMLIB jmp .rc endp Code: ;; @name conv._qfp_div ;; @desc ;; This is internal procedure - do not call it directly. ;; Divides quad-precision floating point number by dword. ;; @arg dest ;; Pointer to buffer for destination number. ;; Can be same as divident ;; @arg divident ;; Pointer to quad precision floating point number which we divide. ;; Can be same as dest. ;; @arg divisor ;; Unsigned dword by which we divide. ;; @ret ;; CF set on error, otherwise ;; EAX = dest ;; @err ERR_OUT_OF_RANGE ;; Resulting number is too small (below binary 1e-(2147483648 - 95)) ;; When number approaches minimal exponent, FASMLIB rather returns error than reducing ;; precision by using fewer bits of mantissa. ;; @err ERR_INTERNAL ;; Bug in FASMLIB. Please report. ;; @note Quad-precision floating point number consists of 96bit mantissa ;; (3 dwords, low order dword first). Follows 32bit (dword) signed exponent, ;; that holds position of low order bit of mantissa. ;; There is no sign. Number doesn't need to be normalized. proc conv._qfp_div dest, divident, divisor push ebx ecx edx esi edi pushf ;EDI = dest mov edi, [dest] ;copy divident to dest (if they aren't same) mov esi, [divident] cmp esi, edi je @f cld movsd movsd movsd movsd @@: ;done if dest mantissa=0 cmp dword [edi], 0 jne taken @f cmp dword [edi+4], 0 jne taken @f cmp dword [edi+8], 0 jne taken @f mov dword [edi+12], 0 ;reset exponent to 0 if mantissa is 0 jmp .done @@: ;EBX = divisor mov ebx, [divisor] ;divide mantissa xor edx, edx mov eax, dword [edi+8] div ebx mov dword [edi+8], eax mov eax, dword [edi+4] div ebx mov dword [edi+4], eax mov eax, dword [edi] div ebx mov dword [edi], eax ;shift in more bits from result, as long as there is place at top of number (eg. upper bit is 0) .another_dword: ;if remainder is 0, we are done, there are no more set bits in result test edx, edx jz .done ;EAX = get another dword of result xor eax, eax div ebx ;insert bits from resulting dword into dest mov ecx, 32 .another_bit: ;if this is last dword of result(EDX=0) and all set bits of this dword are done (EAX=0), then stop test edx, edx jz .done test eax, eax jnz taken @f @@: ;if high order bit is 1, we can't insert any more bits bt dword [edi+8], 31 jc untaken .done ;adjust exponent dec dword [edi+12] jo untaken .error_underflow ;error if we can't get result with enough precision ;shift upper bit from EAX into mantissa shl eax, 1 ;get high bit of EAX to CF rcl dword [edi], 1 ;and shift it into number rcl dword [edi+4], 1 rcl dword [edi+8], 1 jc untaken .error_internal ;continue with next bit dec ecx jnz taken .another_bit ;continue with next dword jmp .another_dword .done: mov eax, edi .rnc: clc .r: popf pop edi esi edx ecx ebx ret .rc: stc jmp .r .error_underflow: mov eax, ERR_OUT_OF_RANGE jmp .rc .error_internal: mov eax, ERR_FASMLIB jmp .rc endp Code: ;routine to extract dword at given bit position in qfp proc conv._qfp_dword qfpnum, index push esi ecx ebx mov esi, [qfpnum] mov ecx, [index] sub ecx, dword [esi+12] ;check which dwords we need cmp ecx, 96 jge .outside cmp ecx, 64 jge .in2 cmp ecx, 32 jge .in1 cmp ecx, 0 jge .in0 cmp ecx, -31 jl .outside .low: xor eax, eax mov ebx, dword [esi] add ecx, 32 shrd eax, ebx, cl jmp .r .in0: mov eax, dword [esi] mov ebx, dword [esi+4] shrd eax, ebx, cl jmp .r .in1: sub ecx, 32 mov eax, dword [esi+4] mov ebx, dword [esi+8] shrd eax, ebx, cl jmp .r .in2: sub ecx, 64 mov eax, dword [esi+8] shr eax, cl jmp .r .outside: xor eax, eax .r: pop ebx ecx esi ret endp Code: ;; @name conv._qfp_mul ;; @desc ;; This is internal procedure - do not call it directly. ;; Multiplies quad-precision floating point number by dword. ;; @arg dest ;; Pointer to buffer for destination number. ;; Can be same as qfpnum ;; @arg qfpnum ;; Pointer to quad precision floating point number which we multiply ;; Can be same as dest. ;; @arg multiplier ;; Unsigned dword by which we multiply. ;; @ret ;; CF set on error, otherwise ;; EAX = dest ;; @err ERR_OUT_OF_RANGE ;; Resulting number is too large (above or equal binary 1e2147483648) ;; @err ERR_INTERNAL ;; Bug in FASMLIB. Please report. ;; @note Quad-precision floating point number consists of 96bit mantissa ;; (3 dwords, low order dword first). Follows 32bit (dword) signed exponent, ;; that holds position of low order bit of mantissa. ;; There is no sign. Number doesn't need to be normalized. proc conv._qfp_mul dest, qfpnum, multiplier push ebx ecx edx esi edi ;EDI = dest mov edi, [dest] ;ESI = qfpnum mov esi, [qfpnum] ;EBX = multiplier mov ebx, [multiplier] ;copy exponent mov eax, dword [esi+12] mov dword [edi+12], eax ;multiply mantissa mov eax, dword [esi] mul ebx mov dword [edi], eax mov eax, dword [esi+4] mov ecx, edx mul ebx add eax, ecx mov dword [edi+4], eax mov ecx, edx mov eax, dword [esi+8] mul ebx add eax, ecx mov dword [edi+8], eax ;if we have some more bits, then shift them to mantissa mov eax, edx .insert_bit: ;no more bits? test eax, eax jz .done ;shift bit into result shr eax, 1 rcr dword [edi+8], 1 rcr dword [edi+4], 1 rcr dword [edi], 1 ;adjust exponent inc dword [edi+12] jo untaken .error_overflow ;follow with next bit jmp .insert_bit .done: mov eax, edi .rnc: clc .r: pop edi esi edx ecx ebx ret .rc: stc jmp .r .error_overflow: mov eax, ERR_OUT_OF_RANGE jmp .rc endp Code: ;; @name conv._qfp2t ;; @desc ;; This is internal procedure - do not call it directly. ;; Converts quad-precision floating point format to non-normalized ;; tenbyte (80-bit) floating point format, used natively by FPU. ;; @arg dest ;; Pointer to 10 byte buffer for destination number. ;; @arg qfpnum ;; Pointer to quad precision floating point number which we convert ;; @arg sign ;; Sign of destination number (our QFP don't have sign) ;; @ret ;; CF set on error, otherwise ;; EAX = dest ;; @err ERR_OUT_OF_RANGE ;; Resulting number is too large to be converted to tenbyte number ;; (absolute above 1e16384 or under 1e-16383) ;; @err ERR_INTERNAL ;; Bug in FASMLIB. Please report. ;; @note Quad-precision floating point number consists of 96bit mantissa ;; (3 dwords, low order dword first). Follows 32bit (dword) signed exponent, ;; that holds position of low order bit of mantissa. ;; There is no sign. Number doesn't need to be normalized. proc conv._qfp2t dest, qfpnum, sign push ebx ecx edx esi edi ;EDI = dest mov edi, [dest] ;ESI = qfpnum mov esi, [qfpnum] ;ebx = position of highest bit in qfpnum1 mov ecx, 2*32 bsr eax, dword [esi+8] jnz @f sub ecx, 32 bsr eax, dword [esi+4] jnz @f sub ecx, 32 bsr eax, dword [esi] jz .qfp_zero @@: add eax, ecx add eax, dword [esi+12] mov ebx, eax ;save mantissa lea eax, [ebx-31] libcall conv._qfp_dword, esi, eax mov dword [edi+4], eax lea eax, [ebx-63] libcall conv._qfp_dword, esi, eax mov dword [edi], eax ;get exponent mov eax, ebx add eax, 16383 cmp eax, 32767 ja .error_overflow ;set sign in exponent cmp [sign], 0 je @f bts eax, 15 @@: ;save sign+exponent mov word [edi+8], ax jmp .done ;special case: number is zero .qfp_zero: mov dword [edi], 0 mov dword [edi+4], 0 mov word [edi+8], 0 jmp .done .done: mov eax, edi .rnc: clc .r: pop edi esi edx ecx ebx ret .rc: stc jmp .r .error_overflow: mov eax, ERR_OUT_OF_RANGE jmp .rc endp This is the actual routine. You can change the values in 10^e computing to increase speed od routine and decrease accurancy a bit. currently they are all "1", eg. max. accurancy. Code: ;; @name extconv.fp_t ;; @desc ;; Convert floating point from decimal ASCII string representation to ;; FPU double-extended (80bit) floating point number, and returns it in ST0. ;; @ret ;; CF set on error, otherwise ;; ST0 = floating point number ;; @arg string ;; Pointer to string containing floating point number. ;; @arg buflen ;; Size of string's buffer. ;; You can set this to -1 (FFFFFFFFh) if string is quaranteed to be properly zero-terminated. ;; @arg allowsign ;; Sign allowance mode: 0 = no sign accepted, 1 = minus only accepted, 2 = minus and plus accepted. ;; @arg allowexp ;; If 1, exponent is allowed. ;; @err ERR_NOT_FLOAT ;; Number is not proper floating point number. ;; @err ERR_INVALID_MODE ;; allowsign > 2, or allowexp > 1. ;; @err ERR_ZERO_SIZE ;; buflen=0. ;; @warn ;; FPU must be initialized and there must be place on FPU stack. This procedure operates same ;; as FLD instruction. ;; @note ;; If allowexp=0, then "e" character (like any other) immediately behind number simply stops conversion, ;; number is converted with exponent 0, and no error is returned. ;; @note ;; This function doesn't use FPU by itself (only to load result to ST0), and internal ;; computations are done with very precision (96-bit mantissa, 32-bit exponent), so result ;; should usually be very precise. ;; @nodep conv._qfp_add ;; @nodep conv._qfp_mul ;; @nodep conv._qfp_div ;; @nodep conv._qfp2t proc extconv.fp_t string, buflen, allowsign, allowexp locals bn rd 4 temp rd 4 dest rd 4 firstdigit dd ? lastdigit dd ? exp dd ? ;exponent adjustment for mantissa digits behind dot sign dd ? ;sign: 0 is +, 1 is - dot dd ? ;dot flag: if 1, dot was already scanned tbnum rd 3 ;ten-byte number endl push esi ;error is buflen=0 cmp [buflen], 0 je .error_zero_size ;check allowances cmp [allowsign], 2 ja .error_mode cmp [allowexp], 1 ja .error_mode ;ESI = string mov esi, [string] mov [exp], 0 mov [dot], 0 mov [sign], 0 ;-------------------------------------------------- ;scan number lodsb ;check plus cmp [allowsign], 2 jne @f cmp al, '+' jne @f lodsb jmp .sign_done @@: ;check minus cmp [allowsign], 0 je @f cmp al, '-' jne @f mov [sign], 1 lodsb @@: .sign_done: mov [firstdigit], esi dec [firstdigit] ;we are behind first char now ; check first digit cmp al, '0' jb .error_bad_float cmp al, '9' ja .error_bad_float ;now scan digits jmp .scan_mantissa_start .scan_mantissa: lodsb .scan_mantissa_start: cmp al, '.' je .scan_dot cmp al, 'e' je .scan_exponent cmp al, '0' jb .end_scan cmp al, '9' ja .end_scan ;it is digit - if we are behind dot, than adjust exponent cmp [dot], 0 je .scan_mantissa dec [exp] jmp .scan_mantissa .scan_dot: cmp [dot], 0 ;was dot already found? jne .error_bad_float mov [dot], 1 jmp .scan_mantissa .end_scan: lea eax, [esi-2] ;we are now BEHIND last character, which is not part of mantissa mov [lastdigit], eax jmp .scan_done .scan_exponent: ;save address of last digit lea eax, [esi-2] mov [lastdigit], eax ;if exponent is disallowed, then 'e' character ends scanning cmp [allowexp], 0 jne @f jmp .scan_done @@: ;get exponent push ebx ;eax = buflen - (esi - string) = remaining size of buffer mov ebx, esi sub ebx, [string] mov eax, [buflen] sub eax, ebx pop ebx libcall conv.dec_id, esi, eax jc .error_exponent add [exp], eax .scan_done: ;-------------------------------------------------- ; calculate 10^exp ;initialize our floating point bignum mov dword [bn], 1 ;mantissa=1 mov dword [bn+4], 0 mov dword [bn+8], 0 mov dword [bn+12], 0 ;exponent=0 ;check exponent cmp [exp], 0 jg .positive_exponent je .exponent_done ;exponent is negative neg [exp] jmp .negative_exponent ;loop to calculate bn = 10^exp .positive_exponent: ;eax = tentable[min(exp,9) - 1] mov eax, [exp] cmp eax, 0 je .exponent_done cmp eax, 1 jbe @f mov eax, 1 @@: sub [exp], eax dec eax mov eax, [conv.tentable + 4*eax] ;multiply bn with eax libcall conv._qfp_mul, addr bn, addr bn, eax jc .rc jmp .positive_exponent .negative_exponent: ;eax = tentable[min(eax,9) - 1] mov eax, [exp] cmp eax, 0 je .exponent_done cmp eax, 1 jbe @f mov eax, 1 @@: sub [exp], eax dec eax mov eax, [conv.tentable + 4*eax] ;multiply bn with eax libcall conv._qfp_div, addr bn, addr bn, eax jc .rc jmp .negative_exponent .exponent_done: ;-------------------------------------------------- ; fun part - cycle through mantissa to calculate final value mov [temp], 0 mov [temp+4], 0 mov [temp+8], 0 mov [temp+12], 0 mov [dest], 0 mov [dest+4], 0 mov [dest+8], 0 mov [dest+12], 0 ; ESI = last digit of mantissa mov esi, [lastdigit] jmp .first_digit .another_digit: ;end loop? dec esi cmp esi, [firstdigit] jb .parse_done .first_digit: mov al, [esi] ;skip dot cmp al, '.' je .another_digit ;calculate digit*bn sub al, '0' movzx eax, al libcall conv._qfp_mul, addr temp, addr bn, eax jc .rc ;add digit*bn to result libcall conv._qfp_add, addr dest, addr dest, addr temp jc .rc ;multiply bn by 10 libcall conv._qfp_mul, addr bn, addr bn, 10 jc .rc ;continue loop jmp .another_digit .parse_done: ;-------------------------------------------------- ; now convert qfp to tenbyte floating point number libcall conv._qfp2t, addr tbnum, addr dest, [sign] jc .rc fld tbyte [tbnum] .rnc: clc cld .r: pop esi ret .rc: stc jmp .r .error_exponent: ;translate ERR_NOT_DECIMAL to ERR_NOT_FLOAT cmp eax, ERR_NOT_DECIMAL je .error_bad_float jmp .rc .error_mode: mov eax, ERR_INVALID_MODE jmp .rc .error_bad_float: mov eax, ERR_NOT_FLOAT jmp .rc .error_zero_size: mov eax, ERR_ZERO_SIZE jmp .rc endp idata { if used conv.tentable | INCLUDE_ALL_DATA eq 1 conv.tentable dd 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 end if } |
|||
![]() |
|
Dopefish 11 Jan 2008, 04:27
Excellent.
![]() Thank you very much. |
|||
![]() |
|
Goto page Previous 1, 2, 3, 4, 5 Next < Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.