components of a floating point value

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
 
G

Glen Herrmannsfeldt

Sheldon Simms said:
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.

(snip)

There are machines with 128 bit floating point representations, with
something like 112 bits for mantissa.

You might consider whether your code works correctly for FLT_RADIX of 10 or
16. There is work on a standard for FLT_RADIX of 10, though with a slightly
more compact storage format than BCD. IBM has been making machines with
FLT_RADIX of 16 for almost 40 years now.

-- glen
 
S

Sheldon Simms

(snip)

There are machines with 128 bit floating point representations, with
something like 112 bits for mantissa.

I'm aware of this. That's why I mentioned that the code makes the
assumption that it does.
You might consider whether your code works correctly for FLT_RADIX of 10 or
16. There is work on a standard for FLT_RADIX of 10, though with a slightly
more compact storage format than BCD. IBM has been making machines with
FLT_RADIX of 16 for almost 40 years now.

I have tried to make it radix neutral by using the value FLT_RADIX
everywhere it makes sense (except in a comment, I just noticed). Do
you see a problem with the code?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,145
Latest member
web3PRAgeency
Top