Floats on x86 - Examples

Robert Crowther Aug 2026

Whats available?

A staggeringly wide number of instructions, especially if operating on data in parallel. I’ll not cover them.

As basic, these SSE mov variants are specific for floats. They move between XMM registers and load/unload memory locations. Note they can not move between two memory locations, or accept immediates. When handling registers, they place in an initial place (‘low doubleword’),

MOVSS

32‐bit (‘single precision’)

MOVSD

64‐bit (‘double precision’)

These MMX mov variants can be used. They also move between XMM (later XMM) registers and load/unload memory locations, will not move between memory locations, and will not accept immediates,

MOVD

32‐bit (doubleword, ‘single precision’)

MOVQ

64‐bit (‘quadword’, ‘double precision’)

As far as I know, the SSE instructions are different because they do a zero fill. I’ve seen both of these instruction sets used a lot, in examples and generated code.

As usual mov has uses (the instruction moves bits, has no understanding of what it moves, so will not corrupt float values when sizing matches). Variants can move between general registers, memory and handle immediates,

MOV

32‐bit (doubleword, ‘single precision’). Other sizes if provoked

Out there I’ve also seen this SSE2 instruction, which is starting to delve into the madness. Works on XMM and memory locations, with a move,

CVTSS2SD

Single to double precision,

What do these instructions do?

In general, they load and unload values into the float registers. Later on, the instruction sets could run more and more maths operations, much the same as the general resisters. There are also some float/integer conversion operations.

Things to beware of,

Early versions couldn’t reorder numbers within a register

Only came in with SSE2

Immediates can’t be loaded

Which I can’t explain, but has remained true. To load a constant value to a floating point register, the value must be saved in memory (stack, setion, heap, etc.) then moved. Or load a general register then transfer to a float register.

Examples

These examples may not be the best way to do things—for all I know, movss/movsd may be more efficient or robust than movd/movq, or preferred in some situations etc. In defence, you won’t find this information, or with great difficulty, elsewhere on the web. I’ll update if I find out more.

You may also query the examples do not step into the world of SSE streaming—packing numbers into registers. But I give a start that works, yes? Maybe later.

All examples given with a proposed call to a print function, but that’s not needed. Still, that gives the examples a extra real code move to XMM0. Also, these are minimal snippets—I assume you’ll frame with code to compile, establish a main function, set stack and so forth.

How do I get floats into a register?

Section memory to xmm

.rodata
    numFloat64ToPrint: dq 64.987

  ...

.text
  ; also works with movq
  movsd xmm0, [numFloat64ToPrint]
  call [printlnFloat64 wrt ..got]

Section .text to xmm

.text
  numFloat64ToPrint:
    dq 64.987

  ...

  ; also works with movq
  movsd xmm0, [numFloat64ToPrint]
  call [printlnFloat64 wrt ..got]

Immediate via. general register to xmm register

I would like to say like this, with the NASM macros. However, use of the __?float64?__() macro is dependant on knowing it must be loaded into a general register (from online commentary I thought it was only RAX but suspicious GCC code and testing proved otherwise). Otherwise, it acts as if it’s broken,

.text
  mov eax, __?float32?__(32.987)
  movd xmm0, eax
  call  [printFloat32 wrt ..got]

  ; 64-bit immediates must go to a general register before memory
  mov rax, __?float64?__(64.987)
  movq xmm0, rax
  call  [printFloat64 wrt ..got]

About 64‐bit immediates: x74 code gets fussy with 64‐bit immediates. There is no support for 64‐bit immediates into anything but a general register. Not only with floats but integers also. This is by report because the underlying addressing system, even in 64‐bit modes, is 32‐bit. What that has to do with immediates I don’t know (and forcing qword on effective addressing doesn’t work). Still can’t argue that GCC seems to output this kind of code, seemingly meaningless moves to RAX or RDX—maybe because it’s the only way possible.

Tidy the above with a macro package

Those macro calls are in standard NASM macro form. The ‘fp’ macro package, a builtin standard in NASM, can make them neater,

; make float immediates tidier
%use fp

.text
  mov eax, float32(32.987)
  movd xmm0, eax
  call  [printFloat32 wrt ..got]

  ; 64-bit immediates must go to a general register before memory
  mov rax, float64(64.987)
  movq xmm0, rax
  call  [printFloat64 wrt ..got]

Float immediates direct

Switching to another immediate representation, use a website utility or other code to create the hex for float literal/immediates (language compilers may well create assembly code that looks like this),

.text
  mov eax, 0x4203F2B0
  movd xmm0, eax
  call  [printFloat32 wrt ..got]

  mov rax, 0x40503F2B020C49BA
  movq xmm0, rax
  call  [printFloat64 wrt ..got]

Immediate via stack to xmm register

Move float immediates to stack memory first. Examples offset by one item so the use of offsetting can be seen,

  mov dword [rbp - 4],  __?float32?__(32.987)
  movd xmm0, [rbp - 4]
  call  [printFloat32 wrt ..got]

  ; 64-bit immediates must go to a general register before memory
  mov rax, __?float64?__(64.987)
  mov qword [rbp - 8], rax
  movq xmm0, [rbp - 8]
  call  [printFloat64 wrt ..got]

Immediate via heap memory to xmm register

Move float immediates to some place in malloced heap memory first.

extern malloc
extern free

  ...

.text

  ...

  ; alloc some malloc space, assign the space pointer to R15
  mov   rdi, 8
  call  [malloc wrt ..got]
  ; final code should have a malloc fail test here
  mov   qword r15, rax

  ...

  mov dword [r15],  __?float32?__(32.987)
  movd xmm0, [r15]
  call  [printFloat32 wrt ..got]

  ; 64-bit immediates must go to a general register before memory
  mov rax, __?float64?__(64.987)
  mov qword [r15], rax
  movq xmm0, [r15]
  call  [printFloat64 wrt ..got]

  ...

  ; free malloc space
  mov   rdi, r15
  call  [free wrt ..got]

So I can stash floats in memory or general registers?

You may wonder, why ask this question? I had an interest quickly, and maybe you have too. The reasons may be a few. For creation of constants (which will not load directly as immediates). Perhaps you are using registers a lot, and want a float value out of the way. Perhaps you are making calls, and need somewhere to put the float—in the System V call convention, all xmm registers are classed volatile. Maybe you have a patch of memory on hand.

You can do this. Assembly only moves bits. The thing to beware of is the banal statement ‘it’s only ones and zeros’. It’s not—layers of encoding turn ‘1s and 0s’ into something meaningful. So instructions that work on general registers are all integer‐intended, and will do little meaningful for a float value. But you can use memory to stash the values, no problem. In fact, GCC output of very simple float code will stash float numbers both in registers and stack memory. So don’t be concerned about this.

References

An online float converter,

https://www.h-schmidt.net/FloatConverter/IEEE754.html

NASM manual, Floating point constants, mostly about creating constants,

https://www.nasm.us/docs/3.01/nasm03.html#section-3.4.6

NASM manual, floating point macros,

https://www.nasm.us/docs/3.01/nasm07.html#section-7.3