News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

How do you print a floating point number?

Started by nixeagle, May 09, 2012, 02:31:22 AM

Previous topic - Next topic

jj2007

Quote from: nixeagle on May 09, 2012, 05:06:31 AMprintf does not output to CygWin's terminal, only to cmd.exe :(. Hopefully jj2007 still has a copy of float$.

nixeagle,

float$ was an early version of Str$(). You can use MB in parallel to your Masm32 installation just by replacing the masm32rt.inc include with MasmBasic's include line. That cares for most other libraries, exotic stuff has to be added by hand. Test the snippet below, the download is at http://www.masm32.com/board/index.php?topic=12460

; include \masm32\include\masm32rt.inc   ; comment out and replace with...
include \masm32\MasmBasic\MasmBasic.inc   ; download
.data
My$   dd ?
MyR8   real8 88.8e88
MyR4   real4 4444.4444
MyR10   real10 1234567890.1234567890
MyQw   qword 1234567890123456789

.code
start:
   ; Init    ; should be used instead of code, start: to initialise some special arrays
   Let My$="The number is "+Str$(MyR4)+CrLf$      ; assign to a variable, use whatever StdOut routine you like
   print My$, "(using Masm32 print)", 13, 10
   invoke StdOut, My$         ; that should work everywhere
   deb 4, "The number is", MyR4         ; debug macro, very versatile
   movlps xmm0, MyR8
   fldpi      ; put something on the FPU
   mov eax, 12345
   deb 4, "The numbers are", MyR4, f:xmm0, MyR8, MyR10, ST(0), eax      ; xmm regs need f: to be interpreted as floats
   fadd MyR4
   Print Str$("\nOn the FPU:\t%f\n\n", ST(0))      ; with format string: \t=tab, \n=newline, %i=integer, %f=float
   fstp st
   print Str$("The Real8 is\t%f\n", MyR8)        ; a simple %f uses about 7 digits, but you can use e.g. %9f to print more digits; %Hf is 16 digits
   Print Str$("The Real10 is\t%If", MyR10)
   print Str$("The Qword is\t%i\n", MyQw)
   mov eax, 100
   mov ecx, 10
   movlps xmm1, FP8(1111.1111)
   Print Str$("The result is\t%f", eax/ecx*f:xmm1), " - and here is one more: ", Str$("Pi=%Jf\n", PI)   ; MB Print (uppercase P) allows to concatenate several args
   nop
   Inkey Str$("\n\nYour puter has run %3f hours since the last boot, give it a break!", Timer/3600000)
   Exit        ; uppercase E means it's MasmBasic exitprocess, cleans up arrays etc
end start
Output:
The number is 4444.444
(using Masm32 print)
The number is 4444.444
The number is   MyR4            4444.444

The numbers are
MyR4            4444.444
f:xmm0          8.880000000000000e+89
MyR8            8.880000000000000e+89
MyR10           1234567890.12345679
ST(0)           3.14159265358979324
eax             12345

On the FPU:     4447.586

The Real8 is    8.880000e+89
The Real10 is   1234567890.12345679The Qword is 1234567890123456789
The result is   11111.11 - and here is one more: Pi=3.141592653589793238

dedndave

printf outputs to std_out
fprintf outputs to a file stream
sprintf outputs to a string buffer

you can use it for whatever you like once it's in a buffer - like MessageBox, etc

http://msdn.microsoft.com/en-us/library/ybk95axf%28v=vs.71%29.aspx

_snprintf is a little better - you get to specify the buffer size
http://msdn.microsoft.com/en-us/library/2ts7cx93%28v=vs.80%29.aspx

_snprintf_s - "modern" secure version
http://msdn.microsoft.com/en-us/library/f30dzcf6%28v=vs.80%29.aspx


i might add...
i am not a "C" kind of guy - lol
so, i haven't played with all the different variations of printf

i would probably use Ray's lib   :P

nixeagle

Quote from: dedndave on May 09, 2012, 09:48:53 AM
printf outputs to std_out
fprintf outputs to a file stream
sprintf outputs to a string buffer

you can use it for whatever you like once it's in a buffer - like MessageBox, etc

http://msdn.microsoft.com/en-us/library/ybk95axf%28v=vs.71%29.aspx

_snprintf is a little better - you get to specify the buffer size
http://msdn.microsoft.com/en-us/library/2ts7cx93%28v=vs.80%29.aspx

_snprintf_s - "modern" secure version
http://msdn.microsoft.com/en-us/library/f30dzcf6%28v=vs.80%29.aspx


i might add...
i am not a "C" kind of guy - lol
so, i haven't played with all the different variations of printf

i would probably use Ray's lib   :P

Oh, doh! :red. I'll use likely just use sprintf and then use print.

To jj2007: I really wanted to avoid including extra things that have to be downloaded. This whole thing is meant to be a lightweight timing tool for microbenchmarking. If I can get away with the basics that everyone has just by having MASM, that is what I'll do.

If sprintf does not work, I'll just code up a poorman's print_float function that prints the integer part of the number, then multiplies the floating point portion by 1000, shoves that into an integer, take the modulus of it and print that as the last 3 digits after the decimal point. Who needs more than 3 digits on the right side of the decimal point for a timing program that measures in ticks :bdg.

Thanks for the input everyone, I hope to get this finished today, though I need to go through and verify the results are correct with the 4 people who have taken the time to run tests for me :U.

jj2007

Quote from: nixeagle on May 09, 2012, 06:14:08 PM
I really wanted to avoid including extra things that have to be downloaded. This whole thing is meant to be a lightweight timing tool for microbenchmarking. If I can get away with the basics that everyone has just by having MASM, that is what I'll do.

crt_sprintf will do the job for you, don't worry. Str$() is an overkill in this case.

dedndave

some older versions of sprintf did not handle floats
probably not a problem nowdays - hardly anyone using win95 or whatever

if i wanted  to use the crt, i would probably use _snprintf_s (probably not supported in win95 either - lol)
it doesn't look like the masm32 msvcrt inc/lib has that one
but - you can get it with LoadLibrary/GetProcAddress/FreeLibrary

nixeagle

Quote from: dedndave on May 09, 2012, 10:35:42 PM
some older versions of sprintf did not handle floats
probably not a problem nowdays - hardly anyone using win95 or whatever

if i wanted  to use the crt, i would probably use _snprintf_s (probably not supported in win95 either - lol)
it doesn't look like the masm32 msvcrt inc/lib has that one
but - you can get it with LoadLibrary/GetProcAddress/FreeLibrary

Nice to know, problem is I don't know how to use LoadLibrary and friends on windows :bg. So if I'm unable to get sprintf to work, I'll just hack up a poor-man's float printing function that:
  • Prints the integer part of the number,
  • prints a decimal point, Sorry Europeans, I'll be using a period for this!
  • Multiplies the floating point number by 1000,
  • Takes the modulus of the floating point number by 1000,
  • prints the integer part again

The above algorithm ought to produce output that looks something like:
1234.123

Crude, but it ought to work no matter what platform it is on. :lol

jj2007

I've hacked together something.

include \masm32\include\masm32rt.inc

float$ MACRO arg8
  ifndef sprBuffer
.data?
sprBuffer db 40 dup(?)
.data
sprFormat db "%f", 0
.code
  endif
  invoke crt_sprintf, offset sprBuffer, offset sprFormat, arg8
  EXITM <offset sprBuffer>
ENDM

.code
start:
MsgBox 0, float$(FP8(1234.5678)), "Wow, a number:", MB_OK
exit
end start


Attention JWasm doesn't like usiing the macro on the same line as a label, e.g.:
start:   MsgBox 0, float$(FP8(1234.5678)), "Wow, a number:", MB_OK

nixeagle

Quote from: jj2007 on May 10, 2012, 02:30:23 AM
I've hacked together something.

Very nice :bg. While you were off hacking, I was doing the same. Follows is my poor-man's print_float


;; assume pointer to float is in eax
;; float is REAL8
print_float PROC
    push ebx
    push ecx
    push edx
    fld REAL8 PTR [eax]            ; Load, store as integer, and print
    fist DWORD PTR [esp+4]        ; the integer portion of the number.
    mov eax, [esp+4]
    print ustr$(eax)

    print "."                      ; Print decimal point marker.

    mov ebx, 3
  l:
    ;;; now prepare to print first 3 digits after the decimal point.
    mov ecx, 10
    mov [esp+4], ecx
   
    fimul DWORD PTR [esp+4]
    fist DWORD PTR [esp+4]
    mov eax, [esp+4]
    mov ecx, 10
    xor edx, edx
    div ecx
    ;; we want remainder, which is in edx.
    print ustr$(edx)

    dec ebx
    jns l
    fistp DWORD PTR [esp+4]
    pop edx
    pop ecx
    pop ebx
    ret
print_float ENDP

raymond

Quote    fld REAL8 PTR [eax]            ; Load, store as integer, and print
    fist DWORD PTR [esp+4]        ; the integer portion of the number.

Whenever you assume something, you risk being wrong half the time.

Since I haven't seen all your source code, I may be wrong myself in assuming that you are not aware of all the intricacies of using FPU instructions. In case I am right, please read at least the first paragraph of the following:
http://www.ray.masmcode.com/tutorial/fpuchap5.htm#fist
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

nixeagle

Quote from: raymond on May 10, 2012, 04:28:02 PM
Quote    fld REAL8 PTR [eax]            ; Load, store as integer, and print
    fist DWORD PTR [esp+4]        ; the integer portion of the number.

Whenever you assume something, you risk being wrong half the time.

Since I haven't seen all your source code, I may be wrong myself in assuming that you are not aware of all the intricacies of using FPU instructions. In case I am right, please read at least the first paragraph of the following:
http://www.ray.masmcode.com/tutorial/fpuchap5.htm#fist


The stack will not overflow if that is what you are getting at. I know the state of the floating point stack at the time this function is called. If you are talking about something else, I'd love to know :bg.

raymond

Here'e the quote I wanted you to read from the description of the fist instruction:
QuoteThis instruction rounds the value of the TOP data register ST(0) to the nearest integer according to the rounding mode of the RC field in the Control Word,

When you open a program, the FPU contol word defaults at a rounding mode of "Round to nearest, or to even if equidistant (this is the initialized state)"

Therefore, if you are storing a value something like 13.67546 as an integter, it would round it to 14 before storing it, unless you would have changed the RC field of the control word to "truncating". The eventual value displayed would also then be 14.785 according to the code you posted.

If you don't want to start modifying that RC field, there are other precautions you must take to insure getting the correct integer. There are several options.
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

jj2007

The FPU is not trivial. On the other hand, a fixed number of decimals routine can be done with a mere 109 bytes, see attachment.

r8a REAL8 12.3456
r8b REAL8 1234.56
r8c REAL8 1234567.89012345678
r8d REAL8 -0.00123456789 ; negative number
r8e REAL8 1.23456
r8f REAL8 -123.456 ; negative number

start:
print floatx$(r8a), 13, 10
print floatx$(r8b), 13, 10
print floatx$(r8c, 100000000), 13, 10
print floatx$(r8d, 10000000), 13, 10
print floatx$(r8e), 13, 10
print floatx$(r8f), 13, 10


Output:
12.346
1234.560
1234567.89012346
-0.0012346
1.235
-123.456


No MasmBasic, no CRT, just plain Masm32 dwtoa :bg

nixeagle

Quote from: raymond on May 10, 2012, 08:26:50 PM
Here'e the quote I wanted you to read from the description of the fist instruction:
QuoteThis instruction rounds the value of the TOP data register ST(0) to the nearest integer according to the rounding mode of the RC field in the Control Word,

When you open a program, the FPU contol word defaults at a rounding mode of "Round to nearest, or to even if equidistant (this is the initialized state)"

Therefore, if you are storing a value something like 13.67546 as an integter, it would round it to 14 before storing it, unless you would have changed the RC field of the control word to "truncating". The eventual value displayed would also then be 14.785 according to the code you posted.

If you don't want to start modifying that RC field, there are other precautions you must take to insure getting the correct integer. There are several options.

Oh, right. I have it set for truncating mode. :8).

P.S. Really nice website for floating point stuff. :U

nixeagle

Raymond, the source to the program this is for is attached to http://www.masm32.com/board/index.php?topic=18787.msg159632#msg159632 . Feel free to check that out and let me know if I goofed the floating point stuff :bdg.

raymond

Since you seem interested in minimal code, you may also want to consider the following details.

- If all the values you expect to "print" are below 10,000, you could use single precision floats which still give you 7 significant digits, leaving at least 3 for the decimal portion. Even the 7th significant digit would not have much meaning anyway, and you could even consider using single precision for all cases.

- If you intend to continue using your posted code to display the three decimal digits, change the jns l to jnz l after the dec ebx. Otherwise, you print 4 digits by decrementing ebx 4 times (originally set at 3) before its value becomes negative.

- You could also consider the following for printing the decimal portion by adding one instruction and removing a few others.
   fist DWORD PTR [esp+4]        ; the integer portion of the number.
   fisub dword ptr[esp+4]        ; ****** leaves only the fractional part on the FPU ******
   mov eax, [esp+4]
   print ustr$(eax)

   print "."                      ; Print decimal point marker.

   mov ebx, 3
 l:
   mov ecx, 10
   mov [esp+4], ecx
   
   fimul DWORD PTR [esp+4]
   fist DWORD PTR [esp+4]
    fisub dword ptr[esp+4]
   mov edx, [esp+4]
   print ustr$(edx)

   dec ebx
   jnz l


I'll also have a quick look at that code.
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com