From 559f36c437a8b2996a770ba0930d2c1e7b763c9a Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 23 Feb 2015 11:41:07 -0500 Subject: [PATCH] code for decomposing float/double/long double, proper FX2DEC NaN support. --- toolbox/CMakeLists.txt | 1 + toolbox/fpinfo.cpp | 106 +++++++++++++++++++++++++++++++++++++++++ toolbox/fpinfo.h | 29 +++++++++++ toolbox/sane.cpp | 20 ++++++-- 4 files changed, 151 insertions(+), 5 deletions(-) create mode 100644 toolbox/fpinfo.cpp create mode 100644 toolbox/fpinfo.h diff --git a/toolbox/CMakeLists.txt b/toolbox/CMakeLists.txt index 9f5f447..da3e81d 100644 --- a/toolbox/CMakeLists.txt +++ b/toolbox/CMakeLists.txt @@ -28,6 +28,7 @@ set(TOOLBOX_SRC fs_spec.cpp realpath.c dispatch.cpp + fpinfo.cpp ) diff --git a/toolbox/fpinfo.cpp b/toolbox/fpinfo.cpp new file mode 100644 index 0000000..22ed979 --- /dev/null +++ b/toolbox/fpinfo.cpp @@ -0,0 +1,106 @@ +#include "fpinfo.h" + +#include + + +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; +} \ No newline at end of file diff --git a/toolbox/fpinfo.h b/toolbox/fpinfo.h new file mode 100644 index 0000000..4f47cfa --- /dev/null +++ b/toolbox/fpinfo.h @@ -0,0 +1,29 @@ +#ifndef __fpinfo_h__ +#define __fpinfo_h__ + +#include + +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 diff --git a/toolbox/sane.cpp b/toolbox/sane.cpp index 15f1f24..f68adce 100644 --- a/toolbox/sane.cpp +++ b/toolbox/sane.cpp @@ -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)