According to The Swift Programming Language :
For example, 0xFp2 represents 15 ⨉ 2^2, which evaluates to 60. Similarly, 0xFp-2 represents 15 ⨉ 2^(-2), which evaluates to 3.75.
Why is 2 used as base for the exponent instead of 16? I'd have expected 0xFp2 == 15 * (16**2)
instead of 0xFp2 == 15 * (2**2)
Swift's hexadecimal notation for floating-point numbers is just a variation of the notation introduced for C in the C99 standard for both input and output (with the printf %a
format).
The purpose of that notation is to be both easy to interpret by humans and to let the bits of the IEEE 754 representation be somewhat recognizable. The IEEE 754 representation uses base two. Consequently, for a normal floating-point number, when the number before p
is between 1
and 2
, the number after p
is directly the value of the exponent field of the IEEE 754 representation. This is in line with the dual objectives of human-readability and closeness to the bit representation:
$ cat t.c
#include <stdio.h>
int main(){
printf("%a\n", 3.14);
}
$ gcc t.c && ./a.out
0x1.91eb851eb851fp+1
The number 0x1.91eb851eb851fp+1
can be seen to be slightly above 3 because the exponent is 1
and the significand is near 0x1.9
, slightly above 0x1.8
, which indicates the exact middle between two powers of two.
This format helps remember that numbers that have a compact representation in decimal are not necessarily simple in binary. In the example above, 3.14
uses all the digits of the significand to approximate (and even so, it isn't represented exactly).
Hexadecimal is used for the number before the p
, which corresponds to the significand in the IEEE 754 format, because it is more compact than binary. The significand of an IEEE 754 binary64 number requires 13 hexadecimal digits after 0x1.
to represent fully, which is a lot, but in binary 52 digits would be required, which is frankly impractical.
The choice of hexadecimal actually has its drawbacks: because of this choice, the several equivalent representations for the same number are not always easy to recognize as equivalent. For instance, 0x1.3p1
and 0x2.6p0
represent the same number, although their digits have nothing in common. In binary, the two notations would correspond to 0b1.0011p1
and 0b10.011p0
, which would be easier to see as equivalent. To take another example, 3.14
can also be represented as 0xc.8f5c28f5c28f8p-2
, which is extremely difficult to see as the same number as 0x1.91eb851eb851fp+1
. This problem would not exist if the number after the p
represented a power of 16, as you suggest in your question, but unicity of the representation was not an objective when C99 was standardized: closeness to the IEEE 754 representation was.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With