Monday, May 4, 2009
Using FLDPI and FMUL instructions
The FLDPI and FMUL instructions can be used in conjunction with FSIN to generate an accurate result for SIN (PI/2). The FLDPI will load PI in ST0. We can then use FMUL to multiply ST0 with 0.5 to generate PI/2 in ST0. FSIN then computes Sin (PI/2).
Note: Instead of FMUL with an operand of 0.5, you can also use FDIV with an operand of 2.0
------------------------------------------------------------
section .data
data dd 0.5
section .text
global _start
_start:
nop
finit
fldpi ; load PI in ST0
fmul dword [data]
fsin
------------------------------------------------------------
GDB:
10 fldpi ; load PI in ST0
1: $st0 = 0
11 fmul dword [data]
1: $st0 = 3.1415926535897932385128089594061862 ---> PI
12 fsin
1: $st0 = 1.5707963267948966192564044797030931 ---> PI/2
14 mov eax,1
1: $st0 = 1 ---> Sin (PI/2)
------------------------------------------------------------
Note: Instead of FMUL with an operand of 0.5, you can also use FDIV with an operand of 2.0
------------------------------------------------------------
section .data
data dd 0.5
section .text
global _start
_start:
nop
finit
fldpi ; load PI in ST0
fmul dword [data]
fsin
------------------------------------------------------------
GDB:
10 fldpi ; load PI in ST0
1: $st0 = 0
11 fmul dword [data]
1: $st0 = 3.1415926535897932385128089594061862 ---> PI
12 fsin
1: $st0 = 1.5707963267948966192564044797030931 ---> PI/2
14 mov eax,1
1: $st0 = 1 ---> Sin (PI/2)
------------------------------------------------------------
Using the FSIN instruction to compute SINE of an angle
Use the fsin instruction to compute the sine of an angle.
The fsin instruction has an implied operand (top of the floating point stack).
The top of the floating point stack is denoted by ST0 register.
The fld instruction loads the floating point value from the location 'theta' into ST0:
fld dword [theta]
Now the operand is in st0. The fsin instruction now computes the sine of the angle in st0 and overwrites st0 with the result. Note: The operand to fsin is in radians(not degrees). In the example below thetha contains 1.57 ( ~PI/2 radians). So at the end of the program we expect a value of Sin(PI/2) in ST0.
-----------------------------------------------------
section .data
theta dd 1.57
section .text
global _start
_start:
nop
finit
fld dword [theta]
fsin
mov eax,1
mov ebx, 0
int 0x80
-------------------------------------------------
It is easy to see the program-flow with gdb:
9 finit
1: $st0 = 0 -> ST0 = 0
10 fld dword [theta] <--- next instruction to be executed.
1: $st0 = 0
11 fsin <--- next instruction to be executed
1: $st0 = 1.57000005245208740234375 (Result of FLD is in ST0)
13 mov eax,1
1: $st0 = 0.99999968297360224280629845128309796 <--- (Result of FSIN is in ST0)
--------------------------------------------------------------
The value in ST0 is 0.9999 which is close to the expected value of 1. Note that the inaccuracy comes from the fact that our input is 1.57 which is only an approximation of PI/2.
The fsin instruction has an implied operand (top of the floating point stack).
The top of the floating point stack is denoted by ST0 register.
The fld instruction loads the floating point value from the location 'theta' into ST0:
fld dword [theta]
Now the operand is in st0. The fsin instruction now computes the sine of the angle in st0 and overwrites st0 with the result. Note: The operand to fsin is in radians(not degrees). In the example below thetha contains 1.57 ( ~PI/2 radians). So at the end of the program we expect a value of Sin(PI/2) in ST0.
-----------------------------------------------------
section .data
theta dd 1.57
section .text
global _start
_start:
nop
finit
fld dword [theta]
fsin
mov eax,1
mov ebx, 0
int 0x80
-------------------------------------------------
It is easy to see the program-flow with gdb:
9 finit
1: $st0 = 0 -> ST0 = 0
10 fld dword [theta] <--- next instruction to be executed.
1: $st0 = 0
11 fsin <--- next instruction to be executed
1: $st0 = 1.57000005245208740234375 (Result of FLD is in ST0)
13 mov eax,1
1: $st0 = 0.99999968297360224280629845128309796 <--- (Result of FSIN is in ST0)
--------------------------------------------------------------
The value in ST0 is 0.9999 which is close to the expected value of 1. Note that the inaccuracy comes from the fact that our input is 1.57 which is only an approximation of PI/2.
Saturday, May 2, 2009
Representation of Floating point numbers in a microprocessor
A floating point number can be represented in any of the following formats:
a)Single Precision (32 bits)
b)Double precision (64 bits)
c)Double Extended precision (80 bits)
Any floating point number is of the form = mantissa * 2 ^ exponent
Single precision uses 32 bits:
1 bit (bit 31) is for the sign.
8 bits (bits 30:23) is for the exponent. Note that the exponent is biased.
23 bits (bits 22:0) is for the mantissa.
Double precision uses 64 bits:
1 bit (bit 63) is for the sign.
11 bits (bits 62:52) is for the exponent. Note that the exponent is biased.
52 bits (bits 51:0) is for the mantissa.
Double-Extended precision uses 80 bits:
1 bit (bit 79) is for the sign.
15 bits (bits 78:64) is for the exponent. Note that the exponent is biased.
64 bits (bits 63:0) is for the mantissa.
How is a number like 10.25 represented in single precision format?
10.25 (base 10) = 1010.01 (base 2)
The first step is to normalize the binary number. Normalization is to represent the number of the form 1.xxxxxx.
1010.01 = 1.01001 * 2 ^ 3
The above representation is of the form mantissa * 2 ^ exponent.
Mantissa = 1.01001
Exponent = 3
This is the information that is saved in the floating point registers inside the microprocessor. The thing to note is that since the numbers are always normalized (ie; 1.xxxxx) the '1' in the integer portion of the mantissa is not saved explicitly but is implied. In the case of the exponent, instead of saving the 'real' exponent, a biased exponent is saved.
Biased exponent = Real exponent + 127 (for single precision)
For the above example, biased exponent = 3 + 127 = 130.
So we now have,
Mantissa = 01001 (ommiting the 1 and the decimal point)
Exponent=1000 0010 (130 in binary)
Sign bit = 0 (positive number)
Constructing the 32-bit register we have:
bit 31 sign -> 0
bits 30:23 exponent -> 1000 0010
bits 22:0 mantissa -> 01001000000000000000000
So the register would read the following:
0100 0001 0010 0100 0000 0000 0000 0000
In hex:
0x41240000
The same concept described above can be extended to double precision and double extended precision formats. Note that in double-extended precision format, the integer part (that is implied in single/double precision) is included and is specified explicitly in bit 63.
Most engineers use floating point calculators to see how the number is represented in different formats. I recommend the IEEE floating point calculator here:
http://babbage.cs.qc.edu/IEEE-754/Decimal.html
a)Single Precision (32 bits)
b)Double precision (64 bits)
c)Double Extended precision (80 bits)
Any floating point number is of the form = mantissa * 2 ^ exponent
Single precision uses 32 bits:
1 bit (bit 31) is for the sign.
8 bits (bits 30:23) is for the exponent. Note that the exponent is biased.
23 bits (bits 22:0) is for the mantissa.
Double precision uses 64 bits:
1 bit (bit 63) is for the sign.
11 bits (bits 62:52) is for the exponent. Note that the exponent is biased.
52 bits (bits 51:0) is for the mantissa.
Double-Extended precision uses 80 bits:
1 bit (bit 79) is for the sign.
15 bits (bits 78:64) is for the exponent. Note that the exponent is biased.
64 bits (bits 63:0) is for the mantissa.
How is a number like 10.25 represented in single precision format?
10.25 (base 10) = 1010.01 (base 2)
The first step is to normalize the binary number. Normalization is to represent the number of the form 1.xxxxxx.
1010.01 = 1.01001 * 2 ^ 3
The above representation is of the form mantissa * 2 ^ exponent.
Mantissa = 1.01001
Exponent = 3
This is the information that is saved in the floating point registers inside the microprocessor. The thing to note is that since the numbers are always normalized (ie; 1.xxxxx) the '1' in the integer portion of the mantissa is not saved explicitly but is implied. In the case of the exponent, instead of saving the 'real' exponent, a biased exponent is saved.
Biased exponent = Real exponent + 127 (for single precision)
For the above example, biased exponent = 3 + 127 = 130.
So we now have,
Mantissa = 01001 (ommiting the 1 and the decimal point)
Exponent=1000 0010 (130 in binary)
Sign bit = 0 (positive number)
Constructing the 32-bit register we have:
bit 31 sign -> 0
bits 30:23 exponent -> 1000 0010
bits 22:0 mantissa -> 01001000000000000000000
So the register would read the following:
0100 0001 0010 0100 0000 0000 0000 0000
In hex:
0x41240000
The same concept described above can be extended to double precision and double extended precision formats. Note that in double-extended precision format, the integer part (that is implied in single/double precision) is included and is specified explicitly in bit 63.
Most engineers use floating point calculators to see how the number is represented in different formats. I recommend the IEEE floating point calculator here:
http://babbage.cs.qc.edu/IEEE-754/Decimal.html
Labels: Double Extended Precision, Double Precision, Floating point, Single Precision
Subscribe to Posts [Atom]