How to display a floating point number by invoking crt_printf?

Started by vraifreud, September 23, 2007, 09:13:20 AM

Previous topic - Next topic

vraifreud

Hello!
Here's part of my program:

...
.data
a dd 5.3
format db "%f",0ah,0dh,0

.code
...
invoke crt_printf, ADDR format, a
...


The number displayed is 0.000000 instead of 5.3. Can anybody tell me where is the problem?

ramguru

Yeah I find it strange too, why crt_printf will work correctly only with dq:

.data
a dq 5.3
format db "%1.1f",0ah,0dh,0

MichaelW

For the type field character "f" the printf function expects a REAL8 (C type double) passed on the stack. If your data is a REAL4 (C type float) then AFAIK to display it with printf you must convert it to a REAL8. One method is to use the FPU:

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
      a dd 5.3
      format db "%f%c",0
    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    fld a                 ; load value into FPU
    add esp, 8            ; allocate space on the stack for a REAL8
    fstp qword ptr [esp]  ; store value as REAL8
    pop eax               ; pop low-order dword into eax
    pop edx               ; pop high-order dword into edx

    ; Pass both dwords on the stack, in the correct order.
   
    invoke crt_printf, ADDR format, edx::eax, 10

    inkey "Press any key to exit..."
    exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start

eschew obfuscation

ramguru

The above code works also for dt...is that a proper way for dt as well ?

MichaelW

It works because MASM knows the type so it encodes the fld instruction correctly:

a dt 5.3
...
fld  tbyte ptr a

a dq 5.3
...
fld  qword ptr a

a dd 5.3
...
fld  dword ptr a


So it's a proper way for dd, dq, or dt.
eschew obfuscation

ramguru


raymond

Actually, with MASM, if a variable has already been declared as a given type, you don't have to specify the type in the instruction. You need to specify the type only when using indirect addressing.
.data
a1  dd  1.1
a2  dq  1.2
a3  dt  1.4
.code
fld  a1
fld  a2
fld  a3

All the above would be properly loaded according to their declared type.

Raymond
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

MichaelW

Looking at it a day later I see that I failed to make my meaning clear. The tbyte/qword/dword ptr is added by MASM, given "fld a" and the dt/dq/dd declarations.
eschew obfuscation

ramguru

I made a test and got some weird results:
.data
    aa dd 5.01234567890123456789
    bb dq 5.01234567890123456789
    cc dt 5.01234567890123456789
    format db "%.20f",0ah,0dh,0
.code
...
    fld aa                ; load value into FPU
    add esp, 8            ; allocate space on the stack for a REAL8
    fstp qword ptr [esp]  ; store value as REAL8
    pop eax               ; pop low-order dword into eax
    pop edx               ; pop high-order dword into edx
   
    invoke crt_printf, ADDR format, edx::eax;, 10
   
    fld bb
    add esp, 8            ; allocate space on the stack for a REAL8
    fstp qword ptr [esp]  ; store value as REAL8
    pop eax               ; pop low-order dword into eax
    pop edx               ; pop high-order dword into edx
   
    invoke crt_printf, ADDR format, edx::eax;, 10

    fld cc
    add esp, 8            ; allocate space on the stack for a REAL8
    fstp qword ptr [esp]  ; store value as REAL8
    pop eax               ; pop low-order dword into eax
    pop edx               ; pop high-order dword into edx
   
    invoke crt_printf, ADDR format, edx::eax;, 10
    inkey "Press any key to exit..."
    exit

results:
5.01234579086303710000 dd
5.01234567890123460000 dq
5.01234567890123460000 dt
Press any key to exit...

Shouldn't I get more precision using dt ?

MichaelW

All printf sees is a REAL8 value. Some of the DOS compilers supported a long double data type that mapped to a REAL10, and the library functions like printf could display these values, but under Windows long double maps to a REAL8.

And using one of those compilers and this source:

#include <stdio.h>
int main()
{
  long double d = 5.01234567890123456789;
  printf("%.20Lf%c",d,10);
  return 0;
}

I get:

5.01234567890123461000
eschew obfuscation

vraifreud

The fstp instruction maps the tword precision in stack registers back to qword precision, so the final result is the same for dt and dq.

raymond

QuoteShouldn't I get more precision using dt ?

Your code was specific:

fstp qword ptr [esp]  ; store value as REAL8

This would be like truncating the value of PI to 3.14 and then expecting a printing function to display it with a few more decimal digits.

Raymond
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

ramguru

Quote from: raymond on September 24, 2007, 04:06:55 PM
Your code was specific:
fstp qword ptr [esp]  ; store value as REAL8
This would be like truncating the value of PI to 3.14 and then expecting a printing function to display it with a few more decimal digits.
Raymond
It's not my code...
...I was asking if it's correct way to deal with dt (by dt I meant greater precision)
...I was told that it is

BTW I tried FpuFLtoA[, addr cc, 20...] which supposedly converts TBYTE to  a string and got
out -> 5.012345678901235 (only 15 digits!! standard for 64-bits float)
in <- 5.01234567890123456789

So we have a precision table:
REAL4 - 7 digits after the point
REAL8 - 16 digits after the point
REAL10 ~ 20 (by one unreliable source)

That makes me wonder if I ever see an algo that could represent REAL10 as a string

Press any key to exit...  :U "God I love this saying"

raymond

QuoteREAL10 ~ 20 (by one unreliable source)

The maximum you can get is 264-1 which is approx. equal to 1.84*1019. For all practical purposes, that would be 19 total significant digits (integer + fractional digits).
The FpuFLtoA function was designed to return a maximum of 16 significant digits (which you got) to simplify the function since additional digits would very rarely be required.

Quotein <- 5.01234567890123456789

Specifying more than 19 significant digits as input for a REAL10 is a waste of time and CANNOT provide any more precision. Most assemblers would probably chop longer inputs to the first 18 significant digits since the easier way to convert from decimal to a binary REAL10 is with the packed BCD format which allows only 18 digits.

(Similarly, specifying more than 8 significant digits as input for a REAL4, or more than 16 significant digits as input for a REAL8, are also a waste of time.)

QuoteREAL4 - 7 digits after the point
REAL8 - 16 digits after the point

Those should read TOTAL significant digits, and not digits after the point. For example, a REAL4 having 6 integer digits should not be reported with more than 1 digit after the point (Ex.: 765432.1); any additional digit after the point would be meaningless. Effectively, a REAL4 having more than 7 integer digits cannot be considered fully accurate as an integer.

Raymond
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

ramguru

Thanks for clarifying me these things:
dd 4*2=8 total digits
dq 8*2=16 total digits
dt 10*2=20 total digits

Compiler chopping digits is not an excuse... I'm more interested in raw data being converted to float, I'll probably try to write algo myself  :boohoo: