S
Sheldon Simms
I would like to portably extract the components of a floating point value:
sign, exponent, and significand. Based on the C floating point model
described in 5.2.4.2.2, I came up with the following code. There are
a couple of things to note about this code.
1) I convert the significand to an integer value in which the least
significant bit(s) of the integer correspond/s to the least significant
digit of the significand. This means that only as many low-order bits as
needed to encode DBL_MANT_DIG digits of the floating point radix are
significant in the integer value.
2) I assume that an unsigned long long int is large enough to represent
the significand. If this isn't true, the integer value used to represent
the significand will silently overflow.
I am interested in comments relating to the correctness/portability of the
code, pitfalls of the approach, tips on better/easier methods, etc.
#include <float.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
static void extract_double_components (double s)
{
int sign;
double tmp;
tmp = copysign(1.0, s);
sign = +1;
if (tmp < 0.0) sign = -1;
if (isinf(s))
{
if (sign < 0)
printf("-Infinity\n");
else
printf("+Infinity\n");
}
else if (isnan(s))
{
printf("NaN\n");
}
else if (s == 0.0)
{
if (sign < 0)
printf("-0.0\n");
else
printf("+0.0\n");
}
else
{
unsigned idx;
long exponent;
unsigned long long significand;
tmp = fabs(s);
/* find the exponent */
exponent = 0;
if (tmp < 1.0)
{
while (tmp < 1.0)
{
tmp = tmp * FLT_RADIX;
--exponent;
}
}
else
{
while (tmp >= 1.0)
{
tmp = tmp / FLT_RADIX;
++exponent;
}
tmp = tmp * FLT_RADIX; /* return to the range 1..2 */
}
/* find the significand */
significand = 0;
for (idx = 0; idx < DBL_MANT_DIG; ++idx)
{
significand = significand * FLT_RADIX;
tmp = tmp * FLT_RADIX;
if (tmp >= FLT_RADIX)
{
++significand;
tmp = tmp - FLT_RADIX;
}
}
printf("sign:%c significand:%llx exponent:%ld\n",
sign > 0 ? '+' : '-', significand, exponent);
}
}
int main (int argc, char *argv[])
{
double d;
if (argc != 2) { printf("usage: %s <double>\n", argv[0]); return -1; }
d = strtod(argv[1], NULL);
extract_double_components(d);
return 0;
}
A few test runs:
[sheldon@wsxyz mcc]$ ./a.out -Inf
-Infinity
[sheldon@wsxyz mcc]$ ./a.out 3.0
sign:+ significand:18000000000000 exponent:2
[sheldon@wsxyz mcc]$ ./a.out 1.5
sign:+ significand:18000000000000 exponent:1
[sheldon@wsxyz mcc]$ ./a.out 0.75
sign:+ significand:18000000000000 exponent:-1
[sheldon@wsxyz mcc]$ ./a.out 3.141592654
sign:+ significand:1921fb54524550 exponent:2
[sheldon@wsxyz mcc]$ ./a.out 2e-320
sign:+ significand:1fa00000000000 exponent:-1063
[sheldon@wsxyz mcc]$ ./a.out 2e-322
sign:+ significand:14000000000000 exponent:-1069
[sheldon@wsxyz mcc]$ ./a.out 2e-324
+0.0
[sheldon@wsxyz mcc]$ ./a.out -0.0
-0.0
sign, exponent, and significand. Based on the C floating point model
described in 5.2.4.2.2, I came up with the following code. There are
a couple of things to note about this code.
1) I convert the significand to an integer value in which the least
significant bit(s) of the integer correspond/s to the least significant
digit of the significand. This means that only as many low-order bits as
needed to encode DBL_MANT_DIG digits of the floating point radix are
significant in the integer value.
2) I assume that an unsigned long long int is large enough to represent
the significand. If this isn't true, the integer value used to represent
the significand will silently overflow.
I am interested in comments relating to the correctness/portability of the
code, pitfalls of the approach, tips on better/easier methods, etc.
#include <float.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
static void extract_double_components (double s)
{
int sign;
double tmp;
tmp = copysign(1.0, s);
sign = +1;
if (tmp < 0.0) sign = -1;
if (isinf(s))
{
if (sign < 0)
printf("-Infinity\n");
else
printf("+Infinity\n");
}
else if (isnan(s))
{
printf("NaN\n");
}
else if (s == 0.0)
{
if (sign < 0)
printf("-0.0\n");
else
printf("+0.0\n");
}
else
{
unsigned idx;
long exponent;
unsigned long long significand;
tmp = fabs(s);
/* find the exponent */
exponent = 0;
if (tmp < 1.0)
{
while (tmp < 1.0)
{
tmp = tmp * FLT_RADIX;
--exponent;
}
}
else
{
while (tmp >= 1.0)
{
tmp = tmp / FLT_RADIX;
++exponent;
}
tmp = tmp * FLT_RADIX; /* return to the range 1..2 */
}
/* find the significand */
significand = 0;
for (idx = 0; idx < DBL_MANT_DIG; ++idx)
{
significand = significand * FLT_RADIX;
tmp = tmp * FLT_RADIX;
if (tmp >= FLT_RADIX)
{
++significand;
tmp = tmp - FLT_RADIX;
}
}
printf("sign:%c significand:%llx exponent:%ld\n",
sign > 0 ? '+' : '-', significand, exponent);
}
}
int main (int argc, char *argv[])
{
double d;
if (argc != 2) { printf("usage: %s <double>\n", argv[0]); return -1; }
d = strtod(argv[1], NULL);
extract_double_components(d);
return 0;
}
A few test runs:
[sheldon@wsxyz mcc]$ ./a.out -Inf
-Infinity
[sheldon@wsxyz mcc]$ ./a.out 3.0
sign:+ significand:18000000000000 exponent:2
[sheldon@wsxyz mcc]$ ./a.out 1.5
sign:+ significand:18000000000000 exponent:1
[sheldon@wsxyz mcc]$ ./a.out 0.75
sign:+ significand:18000000000000 exponent:-1
[sheldon@wsxyz mcc]$ ./a.out 3.141592654
sign:+ significand:1921fb54524550 exponent:2
[sheldon@wsxyz mcc]$ ./a.out 2e-320
sign:+ significand:1fa00000000000 exponent:-1063
[sheldon@wsxyz mcc]$ ./a.out 2e-322
sign:+ significand:14000000000000 exponent:-1069
[sheldon@wsxyz mcc]$ ./a.out 2e-324
+0.0
[sheldon@wsxyz mcc]$ ./a.out -0.0
-0.0