code for decomposing float/double/long double, proper FX2DEC NaN support.

This commit is contained in:
Kelvin Sherlock 2015-02-23 11:41:07 -05:00
parent 563dd0f485
commit 559f36c437
4 changed files with 151 additions and 5 deletions

View File

@ -28,6 +28,7 @@ set(TOOLBOX_SRC
fs_spec.cpp
realpath.c
dispatch.cpp
fpinfo.cpp
)

106
toolbox/fpinfo.cpp Normal file
View File

@ -0,0 +1,106 @@
#include "fpinfo.h"
#include <machine/endian.h>
static_assert(sizeof(float) == 4, "Unexpected float size");
static_assert(sizeof(double) == 8, "Unexpected double size");
static_assert(sizeof(long double) == 8 || sizeof(long double) == 16, "Unexpected long double size");
void fpinfo::init(float *f)
{
union split {
float f;
uint32_t i;
};
uint32_t i = ((split *)f)->i;
sign = i >> 31;
one = 1;
exp = (i >> 23) & ((1 << 8) - 1);
sig = i & ((1 << 24) - 1);
if (exp == 255) {
if (sig == 0) inf = true;
else nan = true;
return;
}
if (exp == 0) {
// 0 or denormalized.
one = 0;
exp = -126;
return;
}
exp -= 127; // bias
}
void fpinfo::init(double *d)
{
union split {
double d;
uint64_t i;
};
uint64_t i = ((split *)d)->i;
sign = i >> 63;
one = 1;
exp = (i >> 52) & ((1 << 11) - 1);
sig = i & ((UINT64_C(1) << 53) - 1);
if (exp == 2047) {
if (sig == 0) inf = true;
else nan = true;
return;
}
if (exp == 0) {
// 0 or denormalized.
one = 0;
exp = -1022;
return;
}
exp -= 1023; // bias
}
void fpinfo::init(long double *ld)
{
union split {
long double ld;
uint64_t i[2];
};
uint64_t i;
uint32_t sexp;
// this needs to be verified.
#if BYTE_ORDER == LITTLE_ENDIAN
i = ((split *)ld)->i[0];
sexp = ((split *)ld)->i[1];
#else
i = ((split *)ld)->i[1];
sexp = ((split *)ld)->i[0];
#endif
sign = sexp >> 15;
sig = sexp & ((1 << 15) - 1);
one = i >> 63;
sig = i & ((UINT64_C(1) << 63) - 1);
if (exp == 32767) {
if (sig == 0) inf = true;
else nan = true;
return;
}
//
exp -= 16383;
}

29
toolbox/fpinfo.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef __fpinfo_h__
#define __fpinfo_h__
#include <cstdint>
struct fpinfo {
bool sign = false;
bool one = false;
int exp = 0;
uint64_t sig = 0;
bool nan = false;
bool inf = false;
fpinfo(float f) { init(&f); }
fpinfo(double d) { init(&d); }
fpinfo(long double ld) {
if (sizeof(long double) == 16) init(&ld);
if (sizeof(long double) == 8) init((double *)&ld);
}
private:
void init(float *);
void init(double *);
void init(long double *);
};
#endif

View File

@ -39,6 +39,7 @@
#include "stackframe.h"
#include "complex.h"
#include "fpinfo.h"
using ToolBox::Log;
@ -104,6 +105,8 @@ using its_complicated::signbit;
}
// long double is an 80-bit extended with an extra 48-bits of 0 padding.
@ -354,6 +357,9 @@ using its_complicated::signbit;
if (df.digits < 0) df.digits = 0;
fpinfo fpi(s);
//fprintf(stderr, "%02x %02x %08x %016llx\n", fpi.sign, fpi.one, fpi.exp, fpi.sig);
// handle infinity, nan as a special case.
switch (fpclassify(s))
{
@ -364,11 +370,14 @@ using its_complicated::signbit;
return 0;
case FP_NAN:
// NAN type should be encoded in the sig.
// 3.2 CSANELib.o nan() function is broken.
d.sgn = signbit(s);
d.sig = "N40xx000000000000";
d.write(d_adr);
// NAN type encoded in the sig.
{
char buffer[20]; // 16 + 2 needed
snprintf(buffer, 20, "N%016llx", fpi.sig);
d.sgn = signbit(s);
d.sig = buffer;
d.write(d_adr);
}
return 0;
case FP_INFINITE:
@ -383,6 +392,7 @@ using its_complicated::signbit;
}
// normal and subnormal handled here....
#if 0
if (df.style == decform::FIXEDDECIMAL)