Search code examples
assemblyfloating-pointx86-64masmieee-754

Assign a decimal value to a REAL8 local variable using MASM


I'm trying to assign a decimal value into a REAL8 local variable. However, the only quirky way I've found is to convert a decimal number to a IEEE 754 64 bit number.

For example, here's a float MASM 64-bit example that assigns a value of 1.5 to the local variable stop, then subtracts the global start time of 1.1, yielding the correct 0.4 result:

float.asm

option casemap:none

externdef printf : near

   .data
   
    szStop        db "stop: %lf",0
    ALIGN         8
    start         REAL8 1.1    

   .code

main proc
   LOCAL           stop:REAL8
   
   ; Convert decimal number to IEEE 754 64 bit number:
   ; https://www.ultimatesolver.com/en/ieee-754
   ;
   ; 1.5 -> 0011111111111000000000000000000000000000000000000000000000000000 -> 4609434218613702656
   MOV             RAX,4609434218613702656
   MOV             stop,RAX   
   
   FLD             QWORD PTR stop
   FSUB            QWORD PTR start
   FSTP            QWORD PTR stop   
   
   PUSH            RSP
   PUSH            QWORD PTR [RSP]
   AND             SPL,0F0h
   MOV             RDX,stop
   LEA             RCX,szStop
   SUB             RSP,32
   CALL            printf
   LEA             RSP,[RSP+40]
   POP             RSP   

   ret
   
main endp

end

Assembled using these batch commands:

float.bat

@echo on

if not defined DevEnvDir (
  call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Auxiliary\Build\vcvars64.bat"
)

ml64.exe float.asm /link /subsystem:console /defaultlib:kernel32.lib /defaultlib:user32.lib /defaultlib:libcmt.lib

The output is correct:

float

Question

Is there a better way to handle decimal numbers in MASM?
Maybe other assemblers handle floats better?


Solution

  • I'm trying to assign a decimal value into a REAL8 local variable. However, the only quirky way I've found is to convert a decimal number to a IEEE 754 64 bit number.

    MOV             RAX,4609434218613702656
    MOV             stop,RAX
    FLD             QWORD PTR stop
    

    You have hardcoded the decimal number 1.5 in the mov rax instruction. For this you had to convert the number yourself.
    You can do easier and forget about conversion while using the REAL8 directive:

            ...
            db      48h, 0B8h       ; REX prefix, MOV RAX opcode
            REAL8   1.5             ; 64-bit immediate
            mov     stop, rax       ; Loading the local variable
            fld     QWORD PTR stop  ; Loading st0
            ...
    

    Alternatively, put the (hardcoded) number anywhere you like in your program and fld it from there:

            ALIGN   8
    start   REAL8   1.1
    num     REAL8   1.5
            ...
            fld     QWORD PTR num
            ...
    

    or

            ...
            fld     QWORD PTR num
            ...
            ret
            ALIGN   8
    num     REAL8   1.5
    main    endp