The posted code has multiple problems:
f = 3.4e38;
You convert a floating point literal of type double
to type float
, you should probably write f = 3.4e38F;
or f = powf(2, 127);
printf("
b10 val: %e, hex val: %x, n bytes: %i", f, f, sizeof(f));
multiple problems here:
- you pass a
float
, which is actually converted to a double
and passed as such but printf
expects an unsigned int
for %x
: This has undefined behavior. The output is meaningless.
- you pass the value of
sizeof(f)
, which has type size_t
, but printf
expects an int
for %i
: This has undefined behavior, but unlikely to cause problems.
f = 3.5e38;
this is a conversion of a literal of type double
to a float
, but in this case the value of the double
exceeds the range of type float
. This has undefined behavior. It seems to produce INF
but this is not guaranteed by the C Standard. If you wrote f = 3.5e38F;
the compiler would probably detect the overflow as the literal exceeds the range of its defined type.
printf("
b10 val: %e, hex val: %x, n bytes: %i", f, f, sizeof(f));
same problems as above
d_l = pow(2, 1024);
the computation is performed as double
. You should probably instead use the function powl
to compute as long double
.
printf("
b10 val: %Le, hex val: %llx, n bytes: %i", d_l, d_l, sizeof(d_l));
same problems as above. Passing a long double
for %llx
has undefined behavior.
d_l = 2;
no problem here!
printf("
b10 val: %Le, hex val: %llx, n bytes: %i", d_l, d_l, sizeof(d_l));
see above.
You cannot really draw any conclusions from the output, the behavior is undefined for multiple reasons.
Here is a modified version:
#include <math.h>
#include <stdio.h>
char *tohex(char *buf, const void *val, size_t n) {
// convert to hex in little endian format
char *p = buf;
const unsigned char *src = val;
while (n --> 0) {
p += sprintf(p, "%02X", src[n]);
}
return buf;
}
int main() {
char buf[33];
float f;
double d;
long double d_l;
// no overflow
f = 3.4e38; // ~ 2^128
printf("b10 val: %E, %A, hex val: %s, n bytes: %i
",
f, f, tohex(buf, &f, sizeof(f)), (int)sizeof(f));
// overflow once we get past 2^128
f = 3.5e38;
printf("b10 val: %E, %A, hex val: %s, n bytes: %i
",
f, f, tohex(buf, &f, sizeof(f)), (int)sizeof(f));
d = 3.4e38; // ~ 2^128
printf("b10 val: %E, %A, hex val: %s, n bytes: %i
",
d, d, tohex(buf, &d, sizeof(d)), (int)sizeof(d));
d = 3.5e38;
printf("b10 val: %E, %A, hex val: %s, n bytes: %i
",
d, d, tohex(buf, &d, sizeof(d)), (int)sizeof(d));
d_l = powl(2, 1024);
printf("b10 val: %LE, %LA, hex val: %s, n bytes: %i
",
d_l, d_l, tohex(buf, &d_l, sizeof(d_l)), (int)sizeof(d_l));
d_l = 2;
printf("b10 val: %LE, %LA, hex val: %s, n bytes: %i
",
d_l, d_l, tohex(buf, &d_l, sizeof(d_l)), (int)sizeof(d_l));
return 0;
}
Output for OS/X 64-bit:
b10 val: 3.400000E+38, 0X1.FF933CP+127, hex val: 7F7FC99E, n bytes: 4
b10 val: INF, INF, hex val: 7F800000, n bytes: 4
b10 val: 3.400000E+38, 0X1.FF933C78CDFADP+127, hex val: 47EFF933C78CDFAD, n bytes: 8
b10 val: 3.500000E+38, 0X1.074F8C4D3CD7BP+128, hex val: 47F074F8C4D3CD7B, n bytes: 8
b10 val: 1.797693E+308, 0X8P+1021, hex val: 00000001041843FF8000000000000000, n bytes: 16
b10 val: 2.000000E+00, 0X8P-2, hex val: 00000001041840008000000000000000, n bytes: 16
Output for OS/X 32-bit:
b10 val: 3.400000E+38, 0X1.FF933CP+127, hex val: 7F7FC99E, n bytes: 4
b10 val: INF, INF, hex val: 7F800000, n bytes: 4
b10 val: 3.400000E+38, 0X1.FF933C78CDFADP+127, hex val: 47EFF933C78CDFAD, n bytes: 8
b10 val: 3.500000E+38, 0X1.074F8C4D3CD7BP+128, hex val: 47F074F8C4D3CD7B, n bytes: 8
b10 val: 1.797693E+308, 0X8P+1021, hex val: BFF87978000043FF8000000000000000, n bytes: 16
b10 val: 2.000000E+00, 0X8P-2, hex val: BFF87978000040008000000000000000, n bytes: 16
The difference comes from the 48 padding bits in the long double
representation: long double
only has 80 bits on this architecture, the 48 bits in the high order bytes (12 hex digits) are meaningless.