1.25. UNIONS
union uint_float
{
uint32_t i;
float f;
};
float calculate_machine_epsilon(float start)
{
union uint_float v;
v.f=start;
v.i++;
return v.f-start;
}
void main()
{
printf ("%g\n", calculate_machine_epsilon(1.0));
};
What we do here is just treat the fraction part of the IEEE 754 number as integer and add 1 to it. The
resultingfloatingnumberisequaltostarting_value+machine_epsilon,sowejusthavetosubtractthestarting
value (using floating point arithmetic) to measure, what difference one bit reflects in the single precision
(float). Theunionserves here as a way to access IEEE 754 number as a regular integer. Adding 1 to it in
fact adds 1 to thefractionpart of the number, however, needless to say, overflow is possible, which will
add another 1 to the exponent part.
x86
Listing 1.359: Optimizing MSVC 2010
tv130 = 8
_v$ = 8
_start$ = 8
_calculate_machine_epsilon PROC
fld DWORD PTR _start$[esp-4]
fst DWORD PTR _v$[esp-4] ; this instruction is redundant
inc DWORD PTR _v$[esp-4]
fsubr DWORD PTR _v$[esp-4]
fstp DWORD PTR tv130[esp-4] ; \ this instruction pair is also redundant
fld DWORD PTR tv130[esp-4] ; /
ret 0
_calculate_machine_epsilon ENDP
The secondFSTinstruction is redundant: there is no necessity to store the input value in the same place
(the compiler decided to allocate thevvariable at the same point in the local stack as the input argument).
Then it is incremented withINC, as it is a normal integer variable. Then it is loaded into the FPU as a 32-bit
IEEE 754 number,FSUBRdoes the rest of job and the resulting value is stored inST0. The lastFSTP/FLD
instruction pair is redundant, but the compiler didn’t optimize it out.
ARM64
Let’s extend our example to 64-bit:
#include <stdio.h>
#include <stdint.h>
typedef union
{
uint64_t i;
double d;
} uint_double;
double calculate_machine_epsilon(double start)
{
uint_double v;