CHAPTER 22. UNIONS CHAPTER 22. UNIONS
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 resulting floating
number is equal tostarting_value+machine_epsilon, so we just need to subtract the starting 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 thefraction
part of the number, however, needless to say, overflow is possible, which will add another 1 to the exponent part.
22.2.1 x86
Listing 22.5: 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 second FST instruction is redundant: there is no need 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 with INC, as it is a normal integer variable. Then it is loaded into the FPU as a 32-bit IEEE 754
number, FSUBR does the rest of job and the resulting value is stored in ST0.
The last FSTP/FLD instruction pair is redundant, but the compiler didn’t optimize it out.
22.2.2 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;
v.d=start;
v.i++;
return v.d-start;
}
void main()
{
printf ("%g\n", calculate_machine_epsilon(1.0));
};