flat assembler
Message board for the users of flat assembler.

Index > Main > Float to string - strange results

Author
Thread Post new topic Reply to topic
manfred



Joined: 28 Feb 2009
Posts: 43
Location: Racibórz, Poland
manfred 04 Mar 2009, 17:04
Hello!

I wrote function, which prints double precision number to stdout with using FPU (w/o FPU is probably too hard for me now), but it gives strange results like that:
Quote:
manfred@manfred-desktop:/media/Projekty/asm/foo$ ./foo
10000.0
manfred@manfred-desktop:/media/Projekty/asm/foo$ ./foo
1.0
manfred@manfred-desktop:/media/Projekty/asm/foo$ ./foo
10000.0
manfred@manfred-desktop:/media/Projekty/asm/foo$ ./foo
1.0
manfred@manfred-desktop:/media/Projekty/asm/foo$ ./foo
1.0
manfred@manfred-desktop:/media/Projekty/asm/foo$ ./foo
1.0


program code (but I think there is no bugs):
Code:
format ELF executable
entry _start
segment readable executable

include './manfred.inc'

_start:
  
  stdcall printDouble, wynik
  exit 0 ;macro
  
segment readable writable
  wynik dq 10000.0    


And, the probably most important thing - function:
Code:
printDouble:
  push ebp
  mov ebp, esp
  sub esp, 5112
  digits equ ebp - 5112
  fpustate equ ebp - 112
  ten equ ebp - 4
  temp equ ebp - 2
  pushf
  push esi
  push ebx
  fsave [fpustate]
  
  xor esi, esi
  mov word [ten], 10
  fnstcw [temp]
  or word [temp], (0Ch shl 8) ;rounding: cut
  fldcw [temp]
  
  mov eax, [ebp + 8]
  fld qword [eax] ;fpu registers: number
  
  ftst
  fstsw ax
  and ah, 01000101b
  jz .printDouble_ok
  cmp ah, 01000101b
  jnz .printDouble_check
  jmp .printDouble_error
  
  .printDouble_zero:
    push "0.0"
    mov eax, 4
    mov ebx, 1
    mov ecx, esp
    mov edx, 3
    int 80h
    jmp .printDouble_end
  
  .printDouble_check:
    cmp ah, 01000000b
    jz .printDouble_zero
    cmp ah, 1
    jnz .printDouble_error
    fchs
    push "-"
    call putc
  
  .printDouble_ok:
    fild word [ten] ;10, number
    fld st1 ;number, 10, number
    frndint ;integer part, 10, number
    fsub st2, st0 ;integer, 10, fractional
    align 16
    .printDouble_div10:
      fxch st1 ;10, integer, fractional
      fld st1 ;integer, 10, integer, fractional
      fprem ;integer mod 10, 10, integer, fractional
      fistp word [digits + esi] ;10, integer, fractional
      fxch st1 ;integer, 10, fractional
      fdiv st0, st1
      frndint ;integer(integer / 10), 10, fractional
      inc esi
      ftst
      fstsw ax
      sahf
      jnz .printDouble_div10
    fstp st0 ;10, fractional
    fxch st1 ;fractional, 10
    .printDouble_print_i:
      test esi, esi
      jz .printDouble_print_i_end
      dec esi
      mov al, [digits + esi]
      or al, "0"
      push eax
      call putc
      inc ch
      cmp ch, 80
      jbe .printDouble_print_i      
    .printDouble_print_i_end:
      push "."
      call putc
      xor ch, ch
    .printDouble_print_f:
      fmul st0, st1 ;fractional*10, 10
      fist word [temp]
      fstsw ax
      and al, 18h
      jnz .printDouble_error
      mov al, [temp]
      or al, "0"
      push eax
      call putc
      inc ch
      fild word [temp]
      fsubp st1, st0
      ftst 
      fstsw ax
      sahf
      jz .printDouble_end
      cmp ch, 80
      jae .printDouble_end
      jmp .printDouble_print_f
    
  .printDouble_error:
    push "r"
    push "Erro"
    mov eax, 4
    mov ebx, 1
    mov ecx, esp
    mov edx, 5
    int 80h
  
  .printDouble_end:
  frstor [fpustate]
  pop ebx
  pop esi
  popf  
  restore temp
  restore ten
  restore fpustate
  restore digits  
  mov esp, ebp
  pop ebp
  ret 4    


So, where is that bug? I can't see any bug...

Thanks in advance,
Manfred Steinhauer

EDIT: For 6.9 it prints 6.9 or 6.9000000000000003552713678800500929355621337890625 (what is correct), for 666.999 - 6.9 or 666.999000000000023646862246096134185791015625, 321.69 - 3.6 or 321.68999999999999772626324556767940521240234375. So, if result is wrong, it prints first digit of integer (integral?) part, and first digit of fractional part. What the f**k?

_________________
Sorry for my English...
Post 04 Mar 2009, 17:04
View user's profile Send private message Visit poster's website Reply with quote
comrade



Joined: 16 Jun 2003
Posts: 1150
Location: Russian Federation
comrade 04 Mar 2009, 20:04
Some decimal numbers cannot be presented exactly in binary floating-point. See here. The Windows Calculator has special logic to correct for that.
Post 04 Mar 2009, 20:04
View user's profile Send private message Visit poster's website AIM Address Yahoo Messenger MSN Messenger ICQ Number Reply with quote
manfred



Joined: 28 Feb 2009
Posts: 43
Location: Racibórz, Poland
manfred 04 Mar 2009, 21:23
You probably misunderstood me. I said this function randomly prints correct value, or only first digits of integer and fractional part for same number in same executable. About 35% of program runs produces good results (eg. 666.999000...) and 65% gives only that two digits (6.9 in example with 666.999)...
Post 04 Mar 2009, 21:23
View user's profile Send private message Visit poster's website Reply with quote
comrade



Joined: 16 Jun 2003
Posts: 1150
Location: Russian Federation
comrade 04 Mar 2009, 22:16
Ah, yes, I misread.

Can you factor out the int 80h syscall into putchar or something like that, so I can test this on Windows? I see you already have a putc, so what are those other int 80h for?
Post 04 Mar 2009, 22:16
View user's profile Send private message Visit poster's website AIM Address Yahoo Messenger MSN Messenger ICQ Number Reply with quote
manfred



Joined: 28 Feb 2009
Posts: 43
Location: Racibórz, Poland
manfred 04 Mar 2009, 22:26
Int 80h with eax = 4 (sys_write) and ebx = 1 (stdout) is write_to_stdout(ecx = pointer to data, edx = length). I've implemented my own print function, but I can't use it here (in final code I will replace they by print calls, if I discover how can I pass pointer to string built on stack).
Post 04 Mar 2009, 22:26
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20299
Location: In your JS exploiting you and your system
revolution 04 Mar 2009, 23:01
Try inserting an finit at the start. Maybe the precision is not set by the OS before starting your app.
Post 04 Mar 2009, 23:01
View user's profile Send private message Visit poster's website Reply with quote
comrade



Joined: 16 Jun 2003
Posts: 1150
Location: Russian Federation
comrade 04 Mar 2009, 23:22
Shouldn't you be cleaning up the stack after you call putc/int80h?

I refactored your program for Windows, seems to work fine:
Code:
;##########################################################################
; printDouble
; 04.03.2008
;##########################################################################
format PE console 4.0
entry start
;##########################################################################
_TITLE          equ  "printDouble"
_NAME           equ  "printDouble"
_VERSION        equ  "0.0"
_VERSIONTEXT    equ  _VERSION
;##########################################################################
include "%include%/win32am.inc"
include "%include%/equates/kernel32.inc"
include "%include%/equates/user32.inc"
include "%include%/macro/if.inc"
include "%include%/macro/macros.inc"
OFFSET equ
;##########################################################################
;##########################################################################
;##########################################################################
section ".code" code readable executable
;##########################################################################
start:
;##########################################################################
;##########################################################################
        push    ebx esi edi

        stdcall [GetStdHandle],STD_OUTPUT_HANDLE
        mov     [hStdOut],eax

        stdcall printDouble,OFFSET wynik

        pop     edi esi ebx
        stdcall [ExitProcess],0
;##########################################################################
printDouble:
  push ebp 
  mov ebp, esp 
  sub esp, 5112 
  digits equ ebp - 5112 
  fpustate equ ebp - 112 
  ten equ ebp - 4 
  temp equ ebp - 2 
  pushf 
  push esi 
  push ebx 
  fsave [fpustate] 
   
  xor esi, esi 
  mov word [ten], 10 
  fnstcw [temp] 
  or word [temp], (0Ch shl 8) ;rounding: cut 
  fldcw [temp] 
   
  mov eax, [ebp + 8] 
  fld qword [eax] ;fpu registers: number 
   
  ftst 
  fstsw ax 
  and ah, 01000101b 
  jz .printDouble_ok 
  cmp ah, 01000101b 
  jnz .printDouble_check 
  jmp .printDouble_error 
   
  .printDouble_zero: 
    push "0.0" 
    mov eax, 4 
    mov ebx, 1 
    mov ecx, esp 
    mov edx, 3 
    call int80h
    add esp,4
    jmp .printDouble_end 
   
  .printDouble_check: 
    cmp ah, 01000000b 
    jz .printDouble_zero 
    cmp ah, 1 
    jnz .printDouble_error 
    fchs 
    push "-" 
    call putc 
   
  .printDouble_ok: 
    fild word [ten] ;10, number 
    fld st1 ;number, 10, number 
    frndint ;integer part, 10, number 
    fsub st2, st0 ;integer, 10, fractional 
    align 16 
    .printDouble_div10: 
      fxch st1 ;10, integer, fractional 
      fld st1 ;integer, 10, integer, fractional 
      fprem ;integer mod 10, 10, integer, fractional 
      fistp word [digits + esi] ;10, integer, fractional 
      fxch st1 ;integer, 10, fractional 
      fdiv st0, st1 
      frndint ;integer(integer / 10), 10, fractional 
      inc esi 
      ftst 
      fstsw ax 
      sahf 
      jnz .printDouble_div10 
    fstp st0 ;10, fractional 
    fxch st1 ;fractional, 10 
    .printDouble_print_i: 
      test esi, esi 
      jz .printDouble_print_i_end 
      dec esi 
      mov al, [digits + esi] 
      or al, "0" 
      push eax 
      call putc 
      inc ch 
      cmp ch, 80 
      jbe .printDouble_print_i       
    .printDouble_print_i_end: 
      push "." 
      call putc 
      xor ch, ch 
    .printDouble_print_f: 
      fmul st0, st1 ;fractional*10, 10 
      fist word [temp] 
      fstsw ax 
      and al, 18h 
      jnz .printDouble_error 
      mov al, [temp] 
      or al, "0" 
      push eax 
      call putc 
      inc ch 
      fild word [temp] 
      fsubp st1, st0 
      ftst  
      fstsw ax 
      sahf 
      jz .printDouble_end 
      cmp ch, 80 
      jae .printDouble_end 
      jmp .printDouble_print_f 
     
  .printDouble_error: 
    push "r" 
    push "Erro" 
    mov eax, 4 
    mov ebx, 1 
    mov ecx, esp 
    mov edx, 5 
    call int80h
    add esp,8
   
  .printDouble_end: 
  frstor [fpustate] 
  pop ebx 
  pop esi 
  popf   
  restore temp 
  restore ten 
  restore fpustate 
  restore digits   
  mov esp, ebp 
  pop ebp 
  ret 4
;##########################################################################
putc:
        lea     eax,[esp+4]
        push    eax ecx edx
        stdcall [WriteConsole],[hStdOut],eax,1,esp,0
        pop     edx ecx eax
        retn    4
;##########################################################################
int80h:
        cmp     eax,4   ; sys_write
        jne     .end
        cmp     ebx,1   ; stdout
        jne     .end
        push    eax ecx edx
        stdcall [WriteConsole],[hStdOut],ecx,edx,esp,0
        pop     edx ecx eax
.end:
        retn
;##########################################################################
;##########################################################################
;##########################################################################
;##########################################################################
section ".data" data readable writeable
    data import
        library kernel32,"kernel32.dll",user32,"user32.dll"
        include "%include%/api/kernel32.inc"
        include "%include%/api/user32.inc"
    end data
;##########################################################################
;##########################################################################
        ; initialized data
        wynik           dq      699.0
;##########################################################################
        ; uninitialized data
        hStdOut         dd      ?
        argc            dd      ?
        argv            rd      16
;##########################################################################
;##########################################################################
;##########################################################################    
Post 04 Mar 2009, 23:22
View user's profile Send private message Visit poster's website AIM Address Yahoo Messenger MSN Messenger ICQ Number Reply with quote
manfred



Joined: 28 Feb 2009
Posts: 43
Location: Racibórz, Poland
manfred 05 Mar 2009, 06:52
No. Putc is my function and it's calling convention is stdcall. After syscalls I must only destroy string (ouch, I'm not doing it! base register, source index and flags content is destroyed!).

EDIT: Aarrgh! I forgot one fact - functions do not preserve ecx, but I used ch as counter in printing loops. Argh.
Good code:
Code:
_printFloatingPoint:
  push ebp
  mov ebp, esp
  sub esp, 5112
  digits equ ebp - 5112
  fpustate equ ebp - 112
  ten equ ebp - 4
  temp equ ebp - 2
  pushf
  push esi
  push ebx
  fsave [fpustate]
  
  xor esi, esi
  mov word [ten], 10
  finit
  fnstcw [temp]
  or word [temp], (0Ch shl 8) ;rounding: cut
  fldcw [temp]
  
  cmp dword [ebp + 12], 64
  ja ._printFloatingPoint_ld80
  jb ._printFloatingPoint_ld32
  
  mov eax, [ebp + 8]
  fld qword [eax] ;fpu registers: number
  jmp ._printFloatingPoint_start
  
  ._printFloatingPoint_ld80:
  mov eax, [ebp + 8]
  fld tword [eax] ;fpu registers: number
  jmp ._printFloatingPoint_start
  
  ._printFloatingPoint_ld32:
  mov eax, [ebp + 8]
  fld dword [eax] ;fpu registers: number
  
  ._printFloatingPoint_start:
  ftst
  fstsw ax
  and ah, 01000101b
  jz ._printFloatingPoint_ok
  cmp ah, 01000101b
  jnz ._printFloatingPoint_check
  jmp ._printFloatingPoint_error
  
  ._printFloatingPoint_zero:
    push "0.0"
    push 3
    lea eax, [esp + 4]
    push eax
    call print
    add esp, 4
    jmp ._printFloatingPoint_end
  
  ._printFloatingPoint_check:
    cmp ah, 01000000b
    jz ._printFloatingPoint_zero
    cmp ah, 1
    jnz ._printFloatingPoint_error
    fchs
    push "-"
    call putc
  
  ._printFloatingPoint_ok:
    fild word [ten] ;10, number
    fld st1 ;number, 10, number
    frndint ;integer part, 10, number
    fsub st2, st0 ;integer, 10, fractional
    align 16
    ._printFloatingPoint_div10:
      fxch st1 ;10, integer, fractional
      fld st1 ;integer, 10, integer, fractional
      fprem ;integer mod 10, 10, integer, fractional
      fistp word [digits + esi] ;10, integer, fractional
      fxch st1 ;integer, 10, fractional
      fdiv st0, st1
      frndint ;integer(integer / 10), 10, fractional
      inc esi
      ftst
      fstsw ax
      sahf
      jnz ._printFloatingPoint_div10
    fstp st0 ;10, fractional
    fxch st1 ;fractional, 10
    xor bh, bh
    ._printFloatingPoint_print_i:
      test esi, esi
      jz ._printFloatingPoint_print_i_end
      dec esi
      mov al, [digits + esi]
      or al, "0"
      push eax
      call putc
      inc bh
      cmp bh, 80
      jbe ._printFloatingPoint_print_i      
    ._printFloatingPoint_print_i_end:
      push "."
      call putc
      xor bh, bh
    ._printFloatingPoint_print_f:
      fmul st0, st1 ;fractional*10, 10
      fist word [temp]
      fstsw ax
      and al, 18h
      jnz ._printFloatingPoint_error
      mov al, [temp]
      or al, "0"
      push eax
      call putc
      inc bh
      fild word [temp]
      fsubp st1, st0
      ftst 
      fstsw ax
      sahf
      jz ._printFloatingPoint_end
      cmp bh, 21
      jae ._printFloatingPoint_end
      jmp ._printFloatingPoint_print_f
    
  ._printFloatingPoint_error:
    push "r"
    push "Erro"
    push 5
    lea eax, [esp + 4]
    push eax
    call print
    add esp, 8
  
  ._printFloatingPoint_end:
  frstor [fpustate]
  pop ebx
  pop esi
  popf  
  restore temp
  restore ten
  restore fpustate
  restore digits  
  mov esp, ebp
  pop ebp
  ret 8    

_________________
Sorry for my English...
Post 05 Mar 2009, 06:52
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.