© Jo Van Hoey 2019
J. Van HoeyBeginning x64 Assembly Programminghttps://doi.org/10.1007/978-1-4842-5076-1_11

11. Floating-Point Arithmetic

Jo Van Hoey1 
(1)
Hamme, Belgium
 

You already know about integer arithmetic; now we will introduce some floating-point computations. There is nothing difficult here; a floating-point value has a decimal point in it and zero or more decimals. We have two kinds of floating-point numbers: single precision and double precision. Double precision is more accurate because it can handle more significant digits. With that information, you now know enough to run and analyze the sample program in this chapter.

Single vs. Double Precision

For those more curious, here is the story.

A single-precision number is stored in 32 bits: 1 sign bit, 8 exponent bits, and 23 fraction bits.
S      EEEEEEEE      FFFFFFFFFFFFFFFFFFFFFFF
0      1      8      9                     31
A double-precision number is stored in 64 bits: 1 sign bit, 11 exponent bits, and 52 fraction bits.
S      EEEEEEEEEEE      FFFFFFFFFFFFF......FFFFFFFFFF
0      1         11     12                          63

The sign bit is simple. When the number is positive, it is 0; when the number is negative, the sign bit is 1.

The exponent bits are more complicated. Let’s look at a decimal example.
  • 200 = 2.0 × 102

  • 5000.30 = 5.0003 × 103

Here is a binary example:
  • 1101010.01011 = 1.0101001011 x 26 (we moved the point six places to the left)

However, the exponent can be positive, negative, or zero. To make that distinction clear, in the case of single precision, 127 is added to a positive exponent before storing it. That means a zero exponent would be stored as 127. That 127 is called a bias. With double-precision values, the bias is 1023.

In the example above, the 1.0101001011 is called the significand or mantissa. The first bit of the significand is a 1 by assumption (it is ‘normalized’), so it is not stored.

Here is a simple example to show how it works. Use, for example, https://babbage.cs.qc.cuny.edu/IEEE-754/ to verify and experiment:

Single precision, decimal number 10:
  • Decimal 10 is 1010 as a binary integer.

  • Sign bit 0, because the number is positive.

  • Obtain a number in the format b.bbbb. 1.010 is the significand with a leading 1 as required. The leading 1 will not be stored.

  • Hence, the exponent is 3 because we moved the point three places. We add 127 because the exponent is positive, so we obtain 130, which in binary is 10000010.

  • Thus, the decimal single-precision number 10 will be stored as:
    0 10000010       01000000000000000000000
    S EEEEEEEE       FFFFFFFFFFFFFFFFFFFFFFF

    or 41200000 in hexadecimal.

Note that the hexadecimal representation of the same value is different in single precision than in double precision. Why not always use double precision and benefit from the higher precision? Double-precision calculations are slower than single-precision calculations, and the operands use more memory.

If you think this is complicated, you are right. Find an appropriate tool on the Internet to do or at least verify the conversions.

You can encounter 80-bit floating-point numbers in older programs, and these numbers have their own instructions, called FPU instructions. This functionality is a legacy from the past and should not be used in new developments. But you will find FPU instructions in articles on the Internet from time to time.

Let’s do some interesting things.

Coding with Floating-Point Numbers

Listing 11-1 shows the example program.
; fcalc.asm
extern printf
section .data
      number1      dq    9.0
      number2      dq    73.0
      fmt          db    "The numbers are %f and %f",10,0
      fmtfloat     db    "%s %f",10,0
      f_sum        db    "The float sum of %f and %f is %f",10,0
      f_dif        db    "The float difference of %f and %f is %f",10,0
      f_mul        db    "The float product of %f and %f is %f",10,0
      f_div        db    "The float division of %f by %f is %f",10,0
      f_sqrt       db    "The float squareroot of %f is %f",10,0
section .bss
section .text
      global main
main:
    push    rbp
    mov     rbp,rsp
; print the numbers
      movsd       xmm0, [number1]
      movsd       xmm1, [number2]
      mov   rdi,fmt
      mov   rax,2      ; two floats
      call  printf
; sum
    movsd   xmm2, [number1]   ; double precision float into xmm
    addsd   xmm2, [number2]   ; add doube precision to xmm
            ; print the result
            movsd xmm0, [number1]
            movsd xmm1, [number2]
            mov   rdi,f_sum
            mov   rax,3 ; three floats
            call  printf
; difference
    movsd   xmm2, [number1]      ; double precision float into xmm
    subsd   xmm2, [number2]      ; subtract from xmm
            ; print the result
            movsd xmm0, [number1]
            movsd xmm1, [number2]
            mov   rdi,f_dif
            mov   rax,3 ; three floats
            call  printf
; multiplication
    movsd   xmm2, [number1]      ; double precision float into xmm
    mulsd   xmm2, [number2]      ; multiply with xmm
            ; print the result
            mov   rdi,f_mul
            movsd xmm0, [number1]
            movsd xmm1, [number2]
            mov   rax,3 ; three floats
            call  printf
; division
    movsd   xmm2, [number1]      ; double precision float into xmm
    divsd   xmm2, [number2]      ; divide xmm0
            ; print the result
            mov   rdi,f_div
            movsd xmm0, [number1]
            movsd xmm1, [number2]
            mov   rax,1 ; one float
            call  printf
; squareroot
    sqrtsd  xmm1, [number1]      ; squareroot double precision in xmm
            ; print the result
            mov   rdi,f_sqrt
            movsd xmm0, [number1]
            mov   rax,2 ; two floats
            call  printf
; exit
            mov   rsp, rbp
            pop   rbp            ; undo the push at the beginning
            ret
Listing 11-1

fcalc.asm

This is a simple program; in fact, the printing takes more effort than the floating-point calculations.

Use a debugger to step through the program and investigate the registers and memory. Note, for example, how 9.0 and 73.0 are stored in memory addresses number1 and number2; these are the double-precision floating-point values.

Remember that when debugging in SASM, the xmm registers are at the bottom of the register window, in the leftmost part of the ymm registers.

movsd means “move a double precision-floating point value.” There is also movss for single precision. Similarly, there are addss , subss , mulss , divss , and sqrtss instructions.

The rest should be pretty straightforward by now! Figure 11-1 shows the output.
../images/483996_1_En_11_Chapter/483996_1_En_11_Fig1_HTML.jpg
Figure 11-1

fcalc.asm output

Now that you know about the stack, try this: comment out push rbp at the beginning and pop rbp at the end. Make and run the program and see what happens: program crash! The cause for the crash will become clear later, but it has to do with stack alignment.

Summary

In this chapter, you learned the following:
  • The basic use of xmm registers for floating-point calculations

  • The difference between single precision and double precision

  • The instructions movsd, addsd, subsd, mulsd, divsd, and sqrtsd

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.51.153