1

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?

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
vengy
  • 1,548
  • 10
  • 18
  • Note that you normally wouldn't return a `float` or `double` in legacy x87 registers in 64-bit code in the first place; you'd use XMM0. (With a `movsd` load from .rdata, or a `movq xmm0, rax`) – Peter Cordes Jul 24 '22 at 19:14

1 Answers1

2

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
Sep Roland
  • 33,889
  • 7
  • 43
  • 76
  • If you're returning in legacy x87 at all, you might as well store constants a REAL4 if they're exactly representable (like 1.5, which is expressible as a fraction with a power-of-2 denominator, 3/2, but not 1.1 which isn't). The hardware expands to an 80-bit register for free when you load a real4 or real8. (Unlike if you used `movsd xmm0, num`). And normally you'd put a constant in `.rdata`, not packed in next to your machine code. None of your examples actually show the normal way! – Peter Cordes Jul 24 '22 at 19:16
  • 1
    Also, I guess MASM doesn't have an equivalent [for NASM](https://stackoverflow.com/questions/66476230/use-of-nasms-float-macros) `mov rax, __?float64?__(1.1)`? That avoids the manual encoding of either IEEE fp constants, or x86-64 machine code, while still letting you use it as an immediate. – Peter Cordes Jul 24 '22 at 19:26
  • @PeterCordes I don't think that in this kind of questions the actual value is of any importance, be it 1.5 or any other value. The question did complain about having to manually convert and avoiding such a manual conversion is what my answer is about, with a nice trick that produces the exact same code. If they really want better optimizations, I would suggest posting on CodeReview. My 'bad examples' solely exist to prove that they can store the number **anywhere** in the program. – Sep Roland Jul 24 '22 at 19:31
  • Also, using float32 for `1.5` would allow `mov dword ptr [stop], imm32`. – Peter Cordes Jul 24 '22 at 19:31
  • Tried building with db 0x48, 0xB8 ; REX prefix, MOV RAX opcode but ran into this error: Microsoft (R) Macro Assembler (x64) Version 14.29.30141.0 Copyright (C) Microsoft Corporation. All rights reserved. Assembling: float.asm float.asm(19) : error A2206:missing operator in expression – vengy Jul 24 '22 at 20:56
  • @vengy Perhaps MASM only accepts the **h** suffix: `db 48h, 0B8h`. – Sep Roland Jul 24 '22 at 20:59
  • Bingo! This worked db 048h, 0B8h – vengy Jul 24 '22 at 21:00