The first fpp.c, allows you to explore the representation of
floating-point numbers on your machine. Note that machines may have
either `big-endian' or `little-endian' addressing schemes (referring
to the order in which integers and floating-point numbers are stored
in memory). Alpha and Intel chips are little-endian, Sparcs, the
Motorola 68k family used in Macs, and Motorola PowerPC chips, are
big-endian. On the latter machines, you'll need to compile these
programs with the -DBIGENDIAN option to the C compiler.
/*
* Explore IEEE single- and double-precision floats.
*
* Invoked with an argument, it reprints that number as a double, hex
* and binary. Without argument, it reads numbers from stdin. The
* argument is either a double or an integer (hex, with leading '0x',
* eg 0x3f800000) as argument.
*
* $Id: fpp.c 12350 2004-03-10 19:16:05Z norman $
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#if HAVE_ERRNO_H
#include <errno.h>
#else
extern int errno;
#endif
/* bytesex.h defines BIGENDIAN=1 (true) or 0 */
#include "bytesex.h"
#ifndef DOUBLEPRECISION
#define DOUBLEPRECISION 1 /* double-precision by default */
#endif
#if DOUBLEPRECISION
#define EXPONENTWIDTH 11
#define MANTISSAWIDTH 20 /* actually just the part of the
mantissa in the MSB */
#define ZEROMANTISSA(F) ((F).ieee.m == 0 && (F).ieee.m2 == 0)
#define NINTS 2 /* number of ints in a double */
typedef double Float;
#else
#define EXPONENTWIDTH 8
#define MANTISSAWIDTH 23
#define ZEROMANTISSA(F) ((F).ieee.m == 0)
#define NINTS 1 /* number of ints in a float */
typedef float Float;
#endif
/* Create an integer with EXPONENTWIDTH set bits. Relies on
zero-filling when left-shifting. */
#define FULLEXPONENT ~(~0 << EXPONENTWIDTH)
/* Define the structure we'll use to do the manipulations */
typedef union {
Float f;
unsigned int i[NINTS];
struct {
#if DOUBLEPRECISION
#if BIGENDIAN
unsigned int s : 1;
unsigned int e : 11;
unsigned int m : 20;
unsigned int m2 : 32;
#else
unsigned int m2 : 32;
unsigned int m : 20;
unsigned int e : 11;
unsigned int s : 1;
#endif
#else /* DOUBLEPRECISION */
#if BIGENDIAN
unsigned int s : 1;
unsigned int e : 8;
unsigned int m : 23;
#else
unsigned int m : 23;
unsigned int e : 8;
unsigned int s : 1;
#endif
#endif
} ieee;
} ieeefloat;
/* Function prototypes */
/* Utility functions */
int pisnan (const Float darg); /* true if argument is a NaN */
int pisinf (const Float darg); /* true if argument is infinite */
int pisfinite (const Float darg); /* true if argument is finite */
int pisnormal (const Float darg); /* true if argument is normal */
Float pnan (const char *tagp); /* return a NaN */
Float pinfinity (Float darg); /* return +ve or -ve infinity */
/* Functions which do the work of this demo program */
char *pbits (unsigned int i, unsigned int n);
int parse_int (const char *s, unsigned int i[NINTS]);
int reprint_number (char *s, Float farg);
/* Following are utility functions, which are portable */
/* Returns non-zero if argument is a NaN -- all bits in exponent set,
and mantissa non-zero. */
int pisnan (const Float darg)
{
ieeefloat d;
d.f = darg;
#if DOUBLEPRECISION
return (d.ieee.e == FULLEXPONENT && !(d.ieee.m == 0 && d.ieee.m2 == 0));
#else
return (d.ieee.e == FULLEXPONENT && d.ieee.m != 0);
#endif
}
/* returns non-zero if argument is infinity -- all bits in exponent
set, and mantissa zero. */
int pisinf (const Float darg)
{
ieeefloat d;
d.f = darg;
#if DOUBLEPRECISION
return (d.ieee.e == FULLEXPONENT && d.ieee.m == 0 && d.ieee.m2 == 0);
#else
return (d.ieee.e == FULLEXPONENT && d.ieee.m == 0);
#endif
}
/* returns non-zero if argument is finite (normal, subnormal or zero)
-- not all bits in exponent set. */
int pisfinite (const Float darg)
{
ieeefloat d;
d.f = darg;
return (d.ieee.e != FULLEXPONENT);
}
/* Returns non-zero if argument is normal -- exponent is not all-ones
* nor all-zeros.
*
* Note that this refers to the argument as a _double_, and might not
* give the expected results if it's applied to a single which is
* promoted to a double when given as a parameter.
*/
int pisnormal (const Float darg)
{
ieeefloat d;
d.f = darg;
return (d.ieee.e != FULLEXPONENT && d.ieee.e != 0);
}
/* returns a NaN. Argument is ignored */
Float pnan (const char *tagp)
{
ieeefloat d;
d.ieee.s = 0;
d.ieee.e = FULLEXPONENT;
/* Set leading bit in mantissa -- quiet NaN. */
d.ieee.m = (1<<(MANTISSAWIDTH-1));
#if DOUBLEPRECISION
d.ieee.m2 = 0;
#endif
return d.f;
}
/* returns positive or negative infinity, depending on the sign of darg */
Float pinfinity (Float darg)
{
ieeefloat d;
d.f = darg; /* copies sign */
d.ieee.e = FULLEXPONENT;
d.ieee.m = 0;
#if DOUBLEPRECISION
d.ieee.m2 = 0;
#endif
return d.f;
}
/*
* pbits: given an integer i (assumed 32 bits),
* return the rightmost n (<=32) bits of the integer expressed in binary.
*/
char *pbits (unsigned int i, unsigned int n)
{
static char s[33];
char *p;
/* printf ("pbits(%x/%d)", i, n); */
s[n] = '\0'; /* terminate the string */
for (p=s+n-1; p>=s; p--, i>>=1)
*p = ((i&1) ? '1' : '0');
return s;
}
#if DOUBLEPRECISION
/* Take a string of (up to) 16 hex digits, remove leading '0x', and
* any spaces, and convert the first 8 into i[0], and the second 8 into
* i[1]. If the string is shorter than 16 digits, pad the remainder
* with '0'.
*
* Only need this for converting 16-digit strings to d-p floats.
*
* Return non-zero on error.
*/
int parse_int (const char *s, unsigned int i[2])
{
char digits0[9], digits1[9], *p;
int ndigits = 0;
if (!(s[0] == '0' && s[1] == 'x'))
return 1;
p = digits0;
s += 2;
while (ndigits <= 16)
{
while (isspace(*s)) /* skip blanks */
s++;
if (*s == '\0') /* pad remainder */
*p++ = '0';
else
*p++ = *s++;
ndigits++;
if (ndigits == 8) /* done 8 digits - switch to second 8*/
p = digits1;
}
digits0[8] = digits1[8] = '\0'; /* terminate the strings */
i[0] = strtoul (digits0, (char**)NULL, 16); /* ...and convert them */
i[1] = strtoul (digits1, (char**)NULL, 16);
/* printf ("parse_int: digits=%s %s --> %08x %08x\n",
digits0, digits1, i[0], i[1]); */
return 0;
}
#else
/* same, but simpler for single precision */
int parse_int (const char *s, unsigned int i[1])
{
char digits0[9], *p;
int ndigits;
if (!(s[0] == '0' && s[1] == 'x'))
return 1;
p = digits0;
s += 2;
for (ndigits=0; ndigits<=8; ndigits++)
{
while (isspace(*s)) /* skip blanks */
s++;
if (*s == '\0') /* pad remainder */
*p++ = '0';
else
*p++ = *s++;
}
digits0[8] = '\0'; /* terminate the strings */
*i = strtoul (digits0, (char**)NULL, 16); /* ...and convert them */
/* printf ("parse_int: digits=%s --> %08x\n", digits0, *i); */
return 0;
}
#endif
/*
* Convert string to number and display in various formats.
* If s is NULL, then use the number farg instead. If the input
* string cannot be interpreted, return non-zero, or zero on success.
*/
int reprint_number (char *s, Float farg)
{
ieeefloat F;
if (s == NULL)
F.f = farg;
else
{
if (s[0]=='0' && s[1]=='x') { /* it's an integer */
if (parse_int (s, F.i)) {
printf ("can't parse integer <%s>\n", s);
return 1;
}
} else {
char *endptr;
errno = 0;
F.f = strtod (s, &endptr);
if (errno != 0)
return 1;
for (; isspace(*endptr); endptr++)
;
if (*endptr != '\0')
return 1;
}
}
#if DOUBLEPRECISION
/* 49 bits of precision is 49*log_10(2)=14.75 dec.digits of precision */
printf ("double %24.16e\n hex %08x %08x\n",
F.f, F.i[0], F.i[1]);
#else
printf (" float %12.7e\n hex %08x\n", F.f, F.i[0]);
#endif
printf ("binary %s ", pbits (F.ieee.s, 1));
printf ("%s ", pbits (F.ieee.e, EXPONENTWIDTH));
printf ("%s ", pbits (F.ieee.m, MANTISSAWIDTH));
#if DOUBLEPRECISION
printf ("%s", pbits (F.ieee.m2, 32));
#endif
printf ("\n type ");
/* now print out what type of number it is */
if (F.ieee.e == 0)
if (ZEROMANTISSA(F))
printf ("%s zero",
(F.ieee.s ? "Negative" : "Positive"));
else
printf ("Subnormal");
else if (F.ieee.e == FULLEXPONENT)
if (ZEROMANTISSA(F))
printf ("%s infinity",
(F.ieee.s ? "Negative" : "Positive"));
else
printf ("%s NaN",
(F.ieee.m & (1<<(MANTISSAWIDTH-1))
? "Quiet"
: "Signalling"));
else
printf ("Normal");
/* test the pis??? routines */
printf (" (%c%c%c%c)\n",
(pisnan (F.f) ? 'N' : 'n'),
(pisinf (F.f) ? 'I' : 'i'),
(pisfinite (F.f) ? 'F' : 'f'),
(pisnormal (F.f) ? 'L' : 'l'));
return 0;
}
int main (int argc, char **argv)
{
Float f = 0;
printf ("%s-precision %s endian...\n",
(DOUBLEPRECISION ? "Double" : "Single"),
(BIGENDIAN ? "big" : "little"));
if (argc > 1)
reprint_number (argv[1], 0);
else
{
char line[80];
while (fgets (line, sizeof(line), stdin) != NULL)
if (line[0] == '=') {
switch (line[1]) {
case 'n':
reprint_number (NULL, pnan(""));
break;
case 'i':
reprint_number (NULL, pinfinity(+1.0));
break;
case 'z':
reprint_number (NULL, 0);
break;
default:
printf ("eh?\n");
break;
}
} else {
if (reprint_number (line, 0))
printf("Enter float, 0x... integer, =n, =i, =z, "
"or ^D to exit\n");
}
}
exit (0);
}