'How to add two numbers, integer and a float in NASM?

I have this code that is suppose to add two numbers, a float(3.25) and a integer(2).

EDITED:

extern _printf, _scanf
global _main
section .bss
  num1: resb 4
section .data
  format_num: db "%f", 10, 0
section .text
_main:

  mov dword [num1], __float32__(3.25)
  add num1,  2

  sub esp, 8
  fld dword [num1]
  mov dword [num1], eax
  fstp qword [esp]
  push format_num
  call _printf
  add esp, 12

ret

The output I get is:

test.asm:11:error: invalid combination of opcode and operands

The output I expect is:

5.250000



Solution 1:[1]

A good tutorial for the x87 FPU is beyond the scope of Stackoverflow but I can recommend the one on MASM forums. Another good source is the Intel Instruction Set Reference. In particular most functions starting with F are the x87 floating point unit (FPU) related instructions.

In general you can't just add a floating point value to an integer. They are two different representations. What you can do is convert the integer to a floating point value and then do floating point computations with it. In particular the instructions that start with FI are floating point operations that involve a conversion of an integer memory operand to floating point.

There are many ways to skin a cat, but if you review the FPU tutorial linked to above you may realize that one simplistic way is to do this:

sub esp, 8           ; Allocate space on stack to store integer
mov dword [esp], 2   ; Move the 32-bit integer value onto stack temporarily
fild dword [esp]     ; Load integer 2 from stack into top of FPU stack at st(0)
                     ;    Converting it to 2.0 in the process
mov dword [esp], __float32__(3.25)
                     ; Move 3.25 onto stack temporarily
fadd dword [esp]     ; Add 3.25 to st(0). Result in st(0). So st(0)=5.25
fstp qword [esp]     ; Store 64-bit double to stack for printf and pop FPU stack.

Rather than using global variables to store values in main memory temporarily I used the stack space we reserve as a temporary staging area to load/operate with the x87 FPU.

If you are on a CPU that supports the SSE2 instruction set (This includes any X86-64 processor in 32-bit mode) then you have other options. One is to use the SIMD instructions and registers to do 32-bit and 64-bit floating point operations. Using the instruction set reference you find some useful instructions like:

  • cvtsi2sd : Convert Dword Integer to Scalar Double-Precision FP Value
  • cvtss2sd : Convert Scalar Single-Precision FP Value to Scalar Double-Precision FP Value
  • addsd : Add Scalar Double-Precision Floating-Point Values
  • movsd : Move Scalar Double-Precision Floating-Point Value

A Scalar Single-Precision FP value is a 32-bit float. A Scalar Double-Precision is a 64-bit double.

sub esp, 8
mov dword [esp], 2      ; Load integer 2 (32-bit signed value) onto stack temporarily
cvtsi2sd xmm0, [esp]    ; Convert 2 on stack to 64-bit float and store in XMM0
mov dword [esp], __float32__(3.25)
                        ; Load 32-bit float value of 3.25 onto stack
cvtss2sd xmm1, [esp]    ; Load 32-bit single and convert it to 64-bit double. Store in XMM1
addsd xmm0, xmm1        ; Add 64-bit float in XMM0 and XMM1 store XMM0
movsd qword [esp], xmm0 ; Move 64-bit float back onto stack to be printed by printf

Solution 2:[2]

The solution is:

extern _printf
global _main
section .bss
  num1: resb 4
  num2: resb 4
section .data
  format_float: db "The result is %f", 10, 0
_main:

  mov dword [num1], 2
  mov dword [num2], __float32__(3.25)

  sub esp, 8
  fild dword [num1]
  fadd dword [num2]
  fstp qword [esp]
  push format_float
  call _printf
  add esp, 12

ret

The output I get is:

5.250000

fild pushes 2 as 2.0000000 into ST0, then fadd can add two float numbers. The result is a float number. int + float = float.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 pppery