Author Topic: Printing of floating point values  (Read 35614 times)

Offline andyz74

  • Jr. Member
  • *
  • Posts: 33
Printing of floating point values
« on: October 24, 2023, 03:08:48 PM »
Hello people,

my question is, is there any (convenient) possibility to print floating point values, DESPITE printf() ?
I have problem with printf, because using gcc as linker, what I have to do in this case, produces alway error saying the "no-PIE"-stuff.  Do I really have to recompile GCC with the -np-PIE flag to work for me, or is there another possibility to print-out floating point-values?

My system is Debian 12, 64 bit.

Greetings, Andy

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Printing of floating point values
« Reply #1 on: October 26, 2023, 02:34:58 AM »
Hi Andy,
Welcome to the forum!

I think there should be a way to get gcc to do what you want. Not really a "Nasm question" but you need to know the answer ! , You can print floats without the C library, too but it may not be the best use of your time.

I hope someone can cone up with the right command line to gcc. 

Best,
Frank



Offline andyz74

  • Jr. Member
  • *
  • Posts: 33
Re: Printing of floating point values
« Reply #2 on: October 26, 2023, 05:22:32 PM »
Hello Frank, and thanks for the welcome-wishes! :-)

As I think all over it, i just need the printf for "seeing" my values. In fact I do not really need this written out. 
But the printf-function seems to be cabaple of interpreting and displaying the packed BCD-Values.

For example I wrote a piece of code, which calculates different squareroots, which are at least partly even, without floating point. These are moved to registers, where I want to see them with a debugger.  Nevertheless, I can't see my values in the register; I assume, they are BCD-packed, which I assume, printf could write them OR I find another solution. (For unpacking the packed BCD ?)

Code: [Select]
; calculates square root

; nasm -f elf64 fpu_sq_root.asm
; ld -s -o fpu_sq_root fpu_sq_root.o


[bits 64]

global _start
 
section .data
    _msg  db '                ',0ah
    len_msg equ $-_msg
    val1: dq 16  ;declare quad word (double precision)
    val2: dq 9  ;declare quad word (double precision)
    val3: dq 4  ;declare quad word (double precision)
    val4: dq 1  ;declare quad word (double precision)
    val5: dq 0  ;declare quad word (double precision)
    val6: dq 0.25  ;declare quad word (double precision)
    val7: dq 2.25  ;declare quad word (double precision)
    val8: dq 6.25  ;declare quad word (double precision)
    val9: dq 12.25  ;declare quad word (double precision)
 
section .bss
    res: resq 1     ;reserve 1 quad word for result
 
section .text
    _start:
    nop
    nop
    nop
    nop
    nop
   
    fld qword [val1] ;load value into st0
    fsqrt           ;compute square root of st0 and store in st0
    fst qword [res] ;store st0 in result
    mov rbx, [res]
    nop

    fld qword [val2] ;load value into st0
    fsqrt           ;compute square root of st0 and store in st0
    fst qword [res] ;store st0 in result
    mov rbx, [res]
    nop

    fld qword [val3] ;load value into st0
    fsqrt           ;compute square root of st0 and store in st0
    fst qword [res] ;store st0 in result
    mov rbx, [res]
    nop

    fld qword [val4] ;load value into st0
    fsqrt           ;compute square root of st0 and store in st0
    fst qword [res] ;store st0 in result
    mov rbx, [res]
    nop

    fld qword [val5] ;load value into st0
    fsqrt           ;compute square root of st0 and store in st0
    fst qword [res] ;store st0 in result
    mov rbx, [res]
    nop

    fld qword [val6] ;load value into st0
    fsqrt           ;compute square root of st0 and store in st0
    fst qword [res] ;store st0 in result
    mov rbx, [res]
    nop
   
    fld qword [val7] ;load value into st0
    fsqrt           ;compute square root of st0 and store in st0
    fst qword [res] ;store st0 in result
    mov rbx, [res]
    nop
   
    fld qword [val8] ;load value into st0
    fsqrt           ;compute square root of st0 and store in st0
    fst qword [res] ;store st0 in result
    mov rbx, [res]
    nop
   
    fld qword [val9] ;load value into st0
    fsqrt           ;compute square root of st0 and store in st0
    fst qword [res] ;store st0 in result
    mov rbx, [res]
    nop
 
nop
nop
nop

mov rax,1
int 80h
    ;end program


=> Squareroots of the val's are computed and one after each stored in RBX.
If I debug the program with radare2, I see senseless numbers in RBX.
Are they packed BCD? If yes, how to unpack ?

Greetz and thanks, Andy

PS : the many "nop" are for me to be able to get orientation while debugging...

Offline fredericopissarra

  • Full Member
  • **
  • Posts: 386
  • Country: br
Re: Printing of floating point values
« Reply #3 on: October 26, 2023, 06:21:19 PM »
You can separate the integer part from the fractional part with cvttss2si or cvttsd2si, and subtract the original values to get the fractional part (remember to obtain the absolute values). Then, for integer part, you can obtain the # of decimal algarisms using log10, and divide the integer value by 10, in sequence, util you get a zero quotient - getting the actual algarisms... For the fractional part, you just need to multiply them by 10 (until get 0.0 or the # of algarisms you want).

There are faster ways...

Offline andyz74

  • Jr. Member
  • *
  • Posts: 33
Re: Printing of floating point values
« Reply #4 on: October 26, 2023, 07:12:13 PM »
You can separate the integer part from the fractional part with cvttss2si or cvttsd2si, and subtract the original values to get the fractional part (remember to obtain the absolute values). Then, for integer part, you can obtain the # of decimal algarisms using log10, and divide the integer value by 10, in sequence, util you get a zero quotient - getting the actual algarisms... For the fractional part, you just need to multiply them by 10 (until get 0.0 or the # of algarisms you want).

There are faster ways...

Ah, many thanks, this is a good way to start! :-)

Offline andyz74

  • Jr. Member
  • *
  • Posts: 33
Re: Printing of floating point values
« Reply #5 on: November 09, 2023, 04:58:44 PM »
OK, so I read a lot, and I try a lot, but unfortunately without much success.  I would be very pleased, if someone could help me further here.

I give a float variable "para_f" with the value 12.34

Then I get the integer part with

Code: [Select]
print_float:
cvttsd2si rdx, [para_f]     ;   rdx ist jetzt ganzzahlteil
ret

...and  with my own print-routine, I can print out the integer part 12.

Then I have a second part, where I try to multiply the "para_f" by 100 and afterwards print out, but I get a "0"

Code: [Select]
print_floatmul100:
movsd xmm0, [para_f] ; single-prec-float nach xmm0
mov dword [varq], 100            ; 100 nach varq und dann nach xmm1
movsd xmm1, [varq]
mulsd xmm0, xmm1   
movsd [varq], xmm0         ;
cvttsd2si rdx, [varq]
ret

I see in debugger, that xmm0 is at last point 4d2 hexadecimal, what is 1234 in decimal.  This is correct.
But then I get output zero, with my print-routine.

Does anyone see, what is my failure, please?

Offline fredericopissarra

  • Full Member
  • **
  • Posts: 386
  • Country: br
Re: Printing of floating point values
« Reply #6 on: November 10, 2023, 11:38:54 AM »
Does anyone see, what is my failure, please?
Yep... Will be 0...

Let's take a look at 12.34 in bynary:
Code: [Select]
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

int main( void )
{
  double d = 12.34;
  uint64_t *p = (uint64_t *)&d;

  printf( "%#" PRIx64 "\n", *p );
}
So, compiling, linking and running:
Code: [Select]
$ cc -O2 -o test test.c
$ ./test
0x4028ae147ae147ae
A floating point value is encoded as described here: https://en.wikipedia.org/wiki/Double-precision_floating-point_format
So, 100 (integer) is a double encoded as ~4.94066e-322 (almost 0). What you meant do to is:
Code: [Select]
; Input xmm0
; Ouput rax
fmult100_trunc:
  mulsd xmm0,[.m100]
  cvttsd2si rax,xmm0
  ret
.m100:
  dq  100.0         ; ".0" is necessary here.

PS: Try to avoid using "memory" variables since you have 16 integer registers and 16 xmm registers available.
PS2: This kind of code works only if the floating point value is in 'integer' range. But notice that a single precision floating point value has a range way larger than that. Take a look at this paper: https://legacy.cs.indiana.edu/~dyb/pubs/FP-Printing-PLDI96.pdf
« Last Edit: November 10, 2023, 11:47:02 AM by fredericopissarra »

Offline andyz74

  • Jr. Member
  • *
  • Posts: 33
Re: Printing of floating point values
« Reply #7 on: November 14, 2023, 07:15:45 PM »
Many thanks, I corrected my code now, and it does, what I want. :-)

Offline andyz74

  • Jr. Member
  • *
  • Posts: 33
Re: Printing of floating point values
« Reply #8 on: March 20, 2025, 05:12:15 PM »
As I am on my way to become the GodMaster of Assembly ... (just joking...;-) )   I gave it another try and it works now for me.  Just as example, that it can be done.

It is a quick and dirty solution, maybe for purpose of checking values while developing, in a macro or *.inc-file

(Linux, 64 bit)

Code: [Select]
; try to print floating-point values via sse2

; nasm -f elf64 float-print.asm && ld -o float-print float-print.o


[bits 64]

global _start
 
section .data
   
    val1 dq 123.454789  ;declare quad word (double precision)
zehn dq 10.0
ziffer db 0
crlf db 13,10
dezp db '.'

section .text

_start:
    call newline
    mov r9, 1 ; r9 starts to count the numbers after decimalpoint we want to write
    movsd xmm1, qword [val1] ; our value, we want to write
    movsd xmm2, xmm1
    cvttsd2si rax, xmm2 ; rax is now val1 without numbers after decimalpoint
    call print_dez
call dez_point
  repeat_it:
cvtsi2sd xmm3, rax ; load the truncated value in xmm3
    subsd xmm2, xmm3 ; xmm2 is now the value behind the decimalpoint
    mulsd xmm2, [zehn] ; multiplic. by ten. (so get one number before the decimalpoint
    cvttsd2si rax, xmm2 ; rax is the number before the decimalpoint (only one number)
    call print_dez ; print this number
    inc r9
    cmp r9, 5 ;  <===   check if the wanted amount of "Nachkommastellen" is reached
    jle repeat_it
   
call newline

mov rax,60
syscall ;end program





print_dez: ; in rax should be the number to write
push rax
push rbx
push rcx
push rdx

xor rcx,rcx
mov rbx,10

schl1:
xor rdx,rdx
div rbx
push rdx
inc rcx
cmp rax,0

jnz schl1

schl2:
pop rdx
add dl,30h
mov [ziffer],dl
push rcx
mov rax, 1 ; Funktionsnummer : schreiben
mov rdi, 1 ; auf STDOUT
mov rsi, ziffer ; was schreiben wir
mov rdx, 1 ; wieviel Zeichen
syscall
pop rcx
loop schl2

pop rdx
pop rcx
pop rbx
pop rax
ret


dez_point: ; just to write the point.
push rax
push rdx
mov rax, 1 ; Funktionsnummer : schreiben
mov rdi, 1 ; auf STDOUT
mov rsi, dezp ; was schreiben wir
mov rdx, 1 ; wieviele Zeichen
syscall
pop rdx
pop rax
ret


newline:
push rax
push rdx
mov rax, 1 ; Funktionsnummer : schreiben
mov rdi, 1 ; auf STDOUT
mov rsi, crlf ; was schreiben wir
mov rdx, 2 ; wieviele Zeichen
syscall
pop rdx
pop rax
ret

Offline fredericopissarra

  • Full Member
  • **
  • Posts: 386
  • Country: br
Re: Printing of floating point values
« Reply #9 on: March 22, 2025, 12:38:03 PM »
Very good... Notice that values like 123.456789 aren't exact in floating point... You can tweak a little bit and create a routine to present the value in a desired precision... Here's an example (it has a small bug, but I'll leave it to you to discover and fix it):
Code: [Select]
; main.asm
  bits  64
  default rel

  section .text

  extern printfloat

  global  _start
_start:
  movss xmm0, [value]
  xor   edi, edi      ; maximum precision.
  call  printfloat

  ; Prints a newline.
  mov   eax,1
  mov   edi,eax
  lea   rsi,[crlf]
  mov   edx,eax
  syscall

  ; Exits the program with errorcode 0.
  mov   eax,60
  xor   edi,edi
  syscall

  section .rodata

  align 4
value:
  dd  123.45678         ; this isn't exact in floating point!

crlf:
  db  `\n`

  section .note.GNU-stack noexec
Code: [Select]
; float.asm
  bits  64
  default rel

  section .text

  global  printfloat

; Input: XMM0 = x; EDI = precision (maximum # of fractional digits - 0 if all).
printfloat:
  cvttss2si r9, xmm0
  mov       byte [rsp-1], 0
  mov       r8d, edi
  lea       rsi, [rsp-1]
  mov       r10d, 0xcccccccd      ; 1/10 scaled.
  mov       edx, r9d

  align 4
.loop1:
  mov       eax, edx
  mov       ecx, edx
  sub       rsi, 1

  imul      rax, r10
  shr       rax, 35               ; EAX = n / 10.
  lea       edi, [rax+rax*4]
  add       edi, edi
  sub       ecx, edi              ; ECX = n % 10.

  add       ecx, '0'
  mov       [rsi], cl
  mov       ecx, edx
  mov       edx, eax
  cmp       ecx, 9                ; reminder sill > 9?
  ja        .loop1                ; yes, stay in the loop.

  mov       eax, 1
  mov       rdx, rsp    ; calc the string size.
  sub       rdx, rsi    ;
  mov       edi, eax
  syscall

  mov       eax,1
  mov       edi,eax
  mov       edx,eax
  lea       rsi, [dot]
  syscall

  pxor      xmm1, xmm1
  mov       r9d, r9d
  cvtsi2ss  xmm1, r9
  subss     xmm0, xmm1
  pxor      xmm1, xmm1
  cvttss2si rdx, xmm0
  mov       edx, edx
  cvtsi2ss  xmm1, rdx
  subss     xmm0, xmm1

  movss     xmm2, [ten]
  mov       r9d, r8d
  lea       rsi, [rsp-1]
  pxor      xmm3, xmm3

  align 4
.loop2:
  mulss     xmm0, xmm2
  pxor      xmm1, xmm1
  mov       edx, edi
  cvttss2si ecx, xmm0
  movsx     eax, cl
  add       ecx, '0'
  cvtsi2ss  xmm1, eax
  mov       [rsi], cl
  mov       eax, edi
  subss     xmm0, xmm1
  syscall

  ; Precision testing...
  test      r8d, r8d
  je        .skip
  sub       r9d, 1
  je        .exit
.skip:

  ; if fractional part isn't zero yet...
  comiss    xmm0, xmm3
  jne       .loop2      ; stay in the loop.

.exit:
  ret

  section .rodata

dot:
  db  '.'

  align 4
ten:
  dd    10.0

  section .note.GNU-stack noexec
Code: [Select]
$ nasm -felf64 main.asm -o main.o
$ nasm -felf64 float.asm -o float.o
$ ld -s -o main main.o float.o
$ ./main
123.45677947998046875

[]s
Fred

Offline andyz74

  • Jr. Member
  • *
  • Posts: 33
Re: Printing of floating point values
« Reply #10 on: March 22, 2025, 06:42:50 PM »
OK, I will look and test it these days and be like every time thankful for reading programs from other people, that I can learn. :-)

But for now, one question :
You have in your program the possibility to control the number of digits after the point. (with EDI) .
EDI = 0 makes ALL numbers after the point.

So, how do we now, how much ALL is? Is there a special sign, that we see, the end of the numbers?

Yes, I overlooked your code, but couldn't find the answer ... lack of knowledge. :-(

Greetz from Germany, Andy

Offline andyz74

  • Jr. Member
  • *
  • Posts: 33
Re: Printing of floating point values
« Reply #11 on: March 23, 2025, 04:42:04 PM »
Ah, I got it. You decrease the fractional part after each loop and check with the
Comiss xmm0, xmm3
If it gets no more smaller...   This is nice. :-)

Offline fredericopissarra

  • Full Member
  • **
  • Posts: 386
  • Country: br
Re: Printing of floating point values
« Reply #12 on: March 25, 2025, 09:44:06 PM »
Ah, I got it. You decrease the fractional part after each loop and check with the
Comiss xmm0, xmm3
If it gets no more smaller...   This is nice. :-)
Of... since it seems you didn't got the three potential bugs there here they are:
  • What will happen if the value is negative?
  • What will happen if the value is too big (like bigger then 264-1?
  • Whay will happen if the value is a NaN?
There is a simple way to fix this... I'll leave it to you...

Offline andyz74

  • Jr. Member
  • *
  • Posts: 33
Re: Printing of floating point values
« Reply #13 on: March 27, 2025, 02:07:47 PM »
I read a bit about the comiss command and the flags that are set, if there were failures, but to be honest, I am absolutely not sure, if this is the right way.