From adfe61d613690d05c3bae978d6fd0cd78a51105b Mon Sep 17 00:00:00 2001 From: Ben Dyer Date: Fri, 21 Aug 2015 17:57:41 +1000 Subject: [PATCH 1/8] Use NumPy half<->float routines to avoid dependence on math functions in C stdlib --- libuavcan/src/marshal/uc_float_spec.cpp | 179 +++++++++++++++--------- 1 file changed, 111 insertions(+), 68 deletions(-) diff --git a/libuavcan/src/marshal/uc_float_spec.cpp b/libuavcan/src/marshal/uc_float_spec.cpp index 18a6e6b05e..6ca25b3604 100644 --- a/libuavcan/src/marshal/uc_float_spec.cpp +++ b/libuavcan/src/marshal/uc_float_spec.cpp @@ -6,87 +6,130 @@ #include #include -#if !defined(UAVCAN_CPP_VERSION) || !defined(UAVCAN_CPP11) -# error UAVCAN_CPP_VERSION -#endif - -#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11 -# include -#endif - namespace uavcan { -/* +/** * IEEE754Converter - * Float16 conversion algorithm: http://half.sourceforge.net/ (MIT License) */ uint16_t IEEE754Converter::nativeNonIeeeToHalf(float value) { - uint16_t hbits = uint16_t(getSignBit(value) ? 0x8000U : 0); - if (areFloatsExactlyEqual(value, 0.0F)) - { - return hbits; + /** + https://github.com/numpy/numpy/blob/master/numpy/core/src/npymath/halffloat.c + BSD license + */ + union { float val; uint32_t valbits; } conv; + uint32_t f_exp, f_sig; + uint16_t h_sgn, h_exp, h_sig; + + conv.val = value; + + h_sgn = uint16_t((conv.valbits & 0x80000000U) >> 16); + f_exp = (conv.valbits & 0x7F800000U); + + /* Exponent overflow/NaN converts to signed inf/NaN */ + if (f_exp >= 0x47800000U) { + if (f_exp == 0x7F800000U) { + /* Inf or NaN */ + f_sig = (conv.valbits & 0x007FFFFFU); + if (f_sig != 0) { + /* NaN - propagate the flag in the significand... */ + return uint16_t(h_sgn | 0x7FFFU); + } else { + /* signed inf */ + return uint16_t(h_sgn + 0x7C00U); + } + } else { + /* overflow to signed inf */ + return uint16_t(h_sgn + 0x7C00U); + } } - if (isNaN(value)) - { - return hbits | 0x7FFFU; + + /* Exponent underflow converts to a subnormal half or signed zero */ + if (f_exp <= 0x38000000U) { + /** + * Signed zeros, subnormal floats, and floats with small + * exponents all convert to signed zero halfs. + */ + if (f_exp < 0x33000000U) { + return h_sgn; + } + + /* Make the subnormal significand */ + f_exp >>= 23; + f_sig = (0x00800000U + (conv.valbits & 0x007FFFFFU)); + f_sig >>= (113 - f_exp); + /* Handle rounding by adding 1 to the bit beyond half precision */ + f_sig += 0x00001000U; + + h_sig = uint16_t(f_sig >> 13); + + /** + * If the rounding causes a bit to spill into h_exp, it will + * increment h_exp from zero to one and h_sig will be zero. + * This is the correct result. + */ + return uint16_t(h_sgn + h_sig); } - if (isInfinity(value)) - { - return hbits | 0x7C00U; - } - int exp; - (void)std::frexp(value, &exp); - if (exp > 16) - { - return hbits | 0x7C00U; - } - if (exp < -13) - { - value = std::ldexp(value, 24); - } - else - { - value = std::ldexp(value, 11 - exp); - hbits |= uint16_t((exp + 14) << 10); - } - const int32_t ival = static_cast(value); - hbits = uint16_t(hbits | (uint32_t((ival < 0) ? (-ival) : ival) & 0x3FFU)); - float diff = std::fabs(value - static_cast(ival)); - hbits = uint16_t(hbits + (diff >= 0.5F)); - return hbits; + + /* Regular case with no overflow or underflow */ + h_exp = uint16_t((f_exp - 0x38000000U) >> 13); + + /* Handle rounding by adding 1 to the bit beyond half precision */ + f_sig = (conv.valbits & 0x007FFFFFU); + f_sig += 0x00001000U; + + h_sig = uint16_t(f_sig >> 13); + + /** + * If the rounding causes a bit to spill into h_exp, it will + * increment h_exp by one and h_sig will be zero. This is the + * correct result. h_exp may increment to 15, at greatest, in + * which case the result overflows to a signed inf. + */ + return uint16_t(h_sgn + h_exp + h_sig); } float IEEE754Converter::halfToNativeNonIeee(uint16_t value) { - float out; - unsigned abs = value & 0x7FFFU; - if (abs > 0x7C00U) - { -#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11 - out = std::numeric_limits::has_quiet_NaN ? std::numeric_limits::quiet_NaN() : 0.0F; -#else - out = nanf(""); -#endif + /** + https://github.com/numpy/numpy/blob/master/numpy/core/src/npymath/halffloat.c + BSD license + */ + union { float ret; uint32_t retbits; } conv; + + uint16_t h_exp, h_sig; + uint32_t f_sgn, f_exp, f_sig; + + h_exp = value & 0x7C00U; + f_sgn = uint32_t(value & 0x8000U) << 16; + switch (h_exp) { + case 0x0000U: /* 0 or subnormal */ + h_sig = (value & 0x03FFU); + if (h_sig == 0) { + /* Signed zero */ + conv.retbits = f_sgn; + } else { + /* Subnormal */ + h_sig = uint16_t(h_sig << 1); + while ((h_sig & 0x0400U) == 0) { + h_sig = uint16_t(h_sig << 1); + h_exp++; + } + f_exp = uint32_t(127 - 15 - h_exp) << 23; + f_sig = uint32_t(h_sig & 0x03FFU) << 13; + conv.retbits = f_sgn + f_exp + f_sig; + } + break; + case 0x7C00U: /* inf or NaN */ + /* All-ones exponent and a copy of the significand */ + conv.retbits = f_sgn + 0x7F800000U + (uint32_t(value & 0x03FFU) << 13); + break; + default: /* normalized */ + /* Just need to adjust the exponent and shift */ + conv.retbits = f_sgn + ((uint32_t(value & 0x7FFFU) + 0x1C000U) << 13); + break; } - else if (abs == 0x7C00U) - { -#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11 - out = std::numeric_limits::has_infinity ? - std::numeric_limits::infinity() : std::numeric_limits::max(); -#else - out = NumericTraits::infinity(); -#endif - } - else if (abs > 0x3FFU) - { - out = std::ldexp(static_cast((value & 0x3FFU) | 0x400U), int(abs >> 10) - 25); - } - else - { - out = std::ldexp(static_cast(abs), -24); - } - return (value & 0x8000U) ? -out : out; + return conv.ret; } } From e97f948b9abbbf129057e73f6c571aa5d1558a5f Mon Sep 17 00:00:00 2001 From: Ben Dyer Date: Fri, 21 Aug 2015 20:35:59 +1000 Subject: [PATCH 2/8] Uncrustified --- libuavcan/src/marshal/uc_float_spec.cpp | 106 +++++++++++++++--------- 1 file changed, 66 insertions(+), 40 deletions(-) diff --git a/libuavcan/src/marshal/uc_float_spec.cpp b/libuavcan/src/marshal/uc_float_spec.cpp index 6ca25b3604..5a13ad3973 100644 --- a/libuavcan/src/marshal/uc_float_spec.cpp +++ b/libuavcan/src/marshal/uc_float_spec.cpp @@ -14,10 +14,15 @@ namespace uavcan uint16_t IEEE754Converter::nativeNonIeeeToHalf(float value) { /** - https://github.com/numpy/numpy/blob/master/numpy/core/src/npymath/halffloat.c - BSD license - */ - union { float val; uint32_t valbits; } conv; + https://github.com/numpy/numpy/blob/master/numpy/core/src/npymath/halffloat.c + BSD license + */ + union + { + float val; + uint32_t valbits; + } conv; + uint32_t f_exp, f_sig; uint16_t h_sgn, h_exp, h_sig; @@ -27,30 +32,39 @@ uint16_t IEEE754Converter::nativeNonIeeeToHalf(float value) f_exp = (conv.valbits & 0x7F800000U); /* Exponent overflow/NaN converts to signed inf/NaN */ - if (f_exp >= 0x47800000U) { - if (f_exp == 0x7F800000U) { + if (f_exp >= 0x47800000U) + { + if (f_exp == 0x7F800000U) + { /* Inf or NaN */ f_sig = (conv.valbits & 0x007FFFFFU); - if (f_sig != 0) { + if (f_sig != 0) + { /* NaN - propagate the flag in the significand... */ return uint16_t(h_sgn | 0x7FFFU); - } else { + } + else + { /* signed inf */ return uint16_t(h_sgn + 0x7C00U); } - } else { + } + else + { /* overflow to signed inf */ return uint16_t(h_sgn + 0x7C00U); } } /* Exponent underflow converts to a subnormal half or signed zero */ - if (f_exp <= 0x38000000U) { + if (f_exp <= 0x38000000U) + { /** * Signed zeros, subnormal floats, and floats with small * exponents all convert to signed zero halfs. */ - if (f_exp < 0x33000000U) { + if (f_exp < 0x33000000U) + { return h_sgn; } @@ -92,44 +106,56 @@ uint16_t IEEE754Converter::nativeNonIeeeToHalf(float value) float IEEE754Converter::halfToNativeNonIeee(uint16_t value) { /** - https://github.com/numpy/numpy/blob/master/numpy/core/src/npymath/halffloat.c - BSD license - */ - union { float ret; uint32_t retbits; } conv; + https://github.com/numpy/numpy/blob/master/numpy/core/src/npymath/halffloat.c + BSD license + */ + union + { + float ret; + uint32_t retbits; + } conv; uint16_t h_exp, h_sig; uint32_t f_sgn, f_exp, f_sig; h_exp = value & 0x7C00U; f_sgn = uint32_t(value & 0x8000U) << 16; - switch (h_exp) { - case 0x0000U: /* 0 or subnormal */ - h_sig = (value & 0x03FFU); - if (h_sig == 0) { - /* Signed zero */ - conv.retbits = f_sgn; - } else { - /* Subnormal */ + switch (h_exp) + { + case 0x0000U: /* 0 or subnormal */ + { + h_sig = (value & 0x03FFU); + if (h_sig == 0) + { + /* Signed zero */ + conv.retbits = f_sgn; + } + else + { + /* Subnormal */ + h_sig = uint16_t(h_sig << 1); + while ((h_sig & 0x0400U) == 0) + { h_sig = uint16_t(h_sig << 1); - while ((h_sig & 0x0400U) == 0) { - h_sig = uint16_t(h_sig << 1); - h_exp++; - } - f_exp = uint32_t(127 - 15 - h_exp) << 23; - f_sig = uint32_t(h_sig & 0x03FFU) << 13; - conv.retbits = f_sgn + f_exp + f_sig; + h_exp++; } - break; - case 0x7C00U: /* inf or NaN */ - /* All-ones exponent and a copy of the significand */ - conv.retbits = f_sgn + 0x7F800000U + (uint32_t(value & 0x03FFU) << 13); - break; - default: /* normalized */ - /* Just need to adjust the exponent and shift */ - conv.retbits = f_sgn + ((uint32_t(value & 0x7FFFU) + 0x1C000U) << 13); - break; + f_exp = uint32_t(127 - 15 - h_exp) << 23; + f_sig = uint32_t(h_sig & 0x03FFU) << 13; + conv.retbits = f_sgn + f_exp + f_sig; + } + break; + } + case 0x7C00U: /* inf or NaN */ + { /* All-ones exponent and a copy of the significand */ + conv.retbits = f_sgn + 0x7F800000U + (uint32_t(value & 0x03FFU) << 13); + break; + } + default: /* normalized */ + { /* Just need to adjust the exponent and shift */ + conv.retbits = f_sgn + ((uint32_t(value & 0x7FFFU) + 0x1C000U) << 13); + break; + } } return conv.ret; } - } From ec1210dfece2ae17ff53cddf860df4005d891152 Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Fri, 21 Aug 2015 14:43:37 +0300 Subject: [PATCH 3/8] Fixed doxygen comments --- libuavcan/src/marshal/uc_float_spec.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/libuavcan/src/marshal/uc_float_spec.cpp b/libuavcan/src/marshal/uc_float_spec.cpp index 5a13ad3973..233de0ec84 100644 --- a/libuavcan/src/marshal/uc_float_spec.cpp +++ b/libuavcan/src/marshal/uc_float_spec.cpp @@ -8,14 +8,14 @@ namespace uavcan { -/** +/* * IEEE754Converter */ uint16_t IEEE754Converter::nativeNonIeeeToHalf(float value) { - /** - https://github.com/numpy/numpy/blob/master/numpy/core/src/npymath/halffloat.c - BSD license + /* + * https://github.com/numpy/numpy/blob/master/numpy/core/src/npymath/halffloat.c + * BSD license */ union { @@ -59,7 +59,7 @@ uint16_t IEEE754Converter::nativeNonIeeeToHalf(float value) /* Exponent underflow converts to a subnormal half or signed zero */ if (f_exp <= 0x38000000U) { - /** + /* * Signed zeros, subnormal floats, and floats with small * exponents all convert to signed zero halfs. */ @@ -77,7 +77,7 @@ uint16_t IEEE754Converter::nativeNonIeeeToHalf(float value) h_sig = uint16_t(f_sig >> 13); - /** + /* * If the rounding causes a bit to spill into h_exp, it will * increment h_exp from zero to one and h_sig will be zero. * This is the correct result. @@ -94,7 +94,7 @@ uint16_t IEEE754Converter::nativeNonIeeeToHalf(float value) h_sig = uint16_t(f_sig >> 13); - /** + /* * If the rounding causes a bit to spill into h_exp, it will * increment h_exp by one and h_sig will be zero. This is the * correct result. h_exp may increment to 15, at greatest, in @@ -105,9 +105,9 @@ uint16_t IEEE754Converter::nativeNonIeeeToHalf(float value) float IEEE754Converter::halfToNativeNonIeee(uint16_t value) { - /** - https://github.com/numpy/numpy/blob/master/numpy/core/src/npymath/halffloat.c - BSD license + /* + * https://github.com/numpy/numpy/blob/master/numpy/core/src/npymath/halffloat.c + * BSD license */ union { From 9c185b3ddf2b362e5f9f8e76005e3945ec3cd2a0 Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Fri, 21 Aug 2015 14:50:29 +0300 Subject: [PATCH 4/8] float16 converter is explicitly declared non-compatible with non-IEEE754 --- libuavcan/include/uavcan/marshal/float_spec.hpp | 11 ++++++----- libuavcan/src/marshal/uc_float_spec.cpp | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/libuavcan/include/uavcan/marshal/float_spec.hpp b/libuavcan/include/uavcan/marshal/float_spec.hpp index b77328f44a..1e8627c052 100644 --- a/libuavcan/include/uavcan/marshal/float_spec.hpp +++ b/libuavcan/include/uavcan/marshal/float_spec.hpp @@ -36,9 +36,10 @@ struct NativeFloatSelector class UAVCAN_EXPORT IEEE754Converter { - // TODO: Non-IEEE float support for float32 and float64 - static uint16_t nativeNonIeeeToHalf(float value); - static float halfToNativeNonIeee(uint16_t value); + // TODO: Non-IEEE float support + + static uint16_t nativeIeeeToHalf(float value); + static float halfToNativeIeee(uint16_t value); IEEE754Converter(); @@ -80,13 +81,13 @@ template <> inline typename IntegerSpec<16, SignednessUnsigned, CastModeTruncate>::StorageType IEEE754Converter::toIeee<16>(typename NativeFloatSelector<16>::Type value) { - return nativeNonIeeeToHalf(value); + return nativeIeeeToHalf(value); } template <> inline typename NativeFloatSelector<16>::Type IEEE754Converter::toNative<16>(typename IntegerSpec<16, SignednessUnsigned, CastModeTruncate>::StorageType value) { - return halfToNativeNonIeee(value); + return halfToNativeIeee(value); } diff --git a/libuavcan/src/marshal/uc_float_spec.cpp b/libuavcan/src/marshal/uc_float_spec.cpp index 233de0ec84..1e2c68bd9d 100644 --- a/libuavcan/src/marshal/uc_float_spec.cpp +++ b/libuavcan/src/marshal/uc_float_spec.cpp @@ -11,7 +11,7 @@ namespace uavcan /* * IEEE754Converter */ -uint16_t IEEE754Converter::nativeNonIeeeToHalf(float value) +uint16_t IEEE754Converter::nativeIeeeToHalf(float value) { /* * https://github.com/numpy/numpy/blob/master/numpy/core/src/npymath/halffloat.c @@ -103,7 +103,7 @@ uint16_t IEEE754Converter::nativeNonIeeeToHalf(float value) return uint16_t(h_sgn + h_exp + h_sig); } -float IEEE754Converter::halfToNativeNonIeee(uint16_t value) +float IEEE754Converter::halfToNativeIeee(uint16_t value) { /* * https://github.com/numpy/numpy/blob/master/numpy/core/src/npymath/halffloat.c From aeb8beadc13ae32da55e65d9e3b7c5564152e856 Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Fri, 21 Aug 2015 15:00:50 +0300 Subject: [PATCH 5/8] IEEE754Converter::enforceIeee<>() --- libuavcan/include/uavcan/marshal/float_spec.hpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libuavcan/include/uavcan/marshal/float_spec.hpp b/libuavcan/include/uavcan/marshal/float_spec.hpp index 1e8627c052..aa2604c9d5 100644 --- a/libuavcan/include/uavcan/marshal/float_spec.hpp +++ b/libuavcan/include/uavcan/marshal/float_spec.hpp @@ -43,6 +43,16 @@ class UAVCAN_EXPORT IEEE754Converter IEEE754Converter(); + template + static void enforceIeee() + { + /* + * Some compilers may have is_iec559 to be defined false despite the fact that IEEE754 is supported. + * An acceptable workaround would be to put an #ifdef here. + */ + StaticAssert::Type>::is_iec559>::check(); + } + public: #if UAVCAN_CPP_VERSION >= UAVCAN_CPP11 /// UAVCAN requires rounding to nearest for all float conversions @@ -53,6 +63,7 @@ public: static typename IntegerSpec::StorageType toIeee(typename NativeFloatSelector::Type value) { + enforceIeee(); union { typename IntegerSpec::StorageType i; @@ -67,6 +78,7 @@ public: static typename NativeFloatSelector::Type toNative(typename IntegerSpec::StorageType value) { + enforceIeee(); union { typename IntegerSpec::StorageType i; From a7fe27a365ddcf74391d959c238cfb580cc62006 Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Fri, 21 Aug 2015 15:03:09 +0300 Subject: [PATCH 6/8] Ninja fix --- libuavcan/include/uavcan/marshal/float_spec.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libuavcan/include/uavcan/marshal/float_spec.hpp b/libuavcan/include/uavcan/marshal/float_spec.hpp index aa2604c9d5..ef3e93b884 100644 --- a/libuavcan/include/uavcan/marshal/float_spec.hpp +++ b/libuavcan/include/uavcan/marshal/float_spec.hpp @@ -50,7 +50,9 @@ class UAVCAN_EXPORT IEEE754Converter * Some compilers may have is_iec559 to be defined false despite the fact that IEEE754 is supported. * An acceptable workaround would be to put an #ifdef here. */ +#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11 StaticAssert::Type>::is_iec559>::check(); +#endif } public: From 37bd23e4faad0f4318e17186ddf640df97157502 Mon Sep 17 00:00:00 2001 From: Ben Dyer Date: Mon, 24 Aug 2015 20:07:03 +1000 Subject: [PATCH 7/8] Replace float<->half implementation with public domain code --- libuavcan/include/uavcan/build_config.hpp | 8 + libuavcan/src/marshal/uc_float_spec.cpp | 178 +++++++--------------- 2 files changed, 59 insertions(+), 127 deletions(-) diff --git a/libuavcan/include/uavcan/build_config.hpp b/libuavcan/include/uavcan/build_config.hpp index 6b8768c3e5..81bde86938 100644 --- a/libuavcan/include/uavcan/build_config.hpp +++ b/libuavcan/include/uavcan/build_config.hpp @@ -136,6 +136,14 @@ # define UAVCAN_USE_EXTERNAL_SNPRINTF 0 #endif +/** + * Allows the user's application to provide a custom implementation of IEEE754Converter::nativeIeeeToHalf and + * IEEE754Converter::halfToNativeIeee. + */ +#ifndef UAVCAN_USE_EXTERNAL_FLOAT16_CONVERSION +# define UAVCAN_USE_EXTERNAL_FLOAT16_CONVERSION 0 +#endif + /** * Run time checks. * Resolves to the standard assert() by default. diff --git a/libuavcan/src/marshal/uc_float_spec.cpp b/libuavcan/src/marshal/uc_float_spec.cpp index 1e2c68bd9d..3254768e43 100644 --- a/libuavcan/src/marshal/uc_float_spec.cpp +++ b/libuavcan/src/marshal/uc_float_spec.cpp @@ -6,156 +6,80 @@ #include #include +#if !UAVCAN_USE_EXTERNAL_FLOAT16_CONVERSION namespace uavcan { +typedef union +{ + uint32_t u; + float f; +} fp32; + /* * IEEE754Converter */ uint16_t IEEE754Converter::nativeIeeeToHalf(float value) { /* - * https://github.com/numpy/numpy/blob/master/numpy/core/src/npymath/halffloat.c - * BSD license + * https://gist.github.com/rygorous/2156668 + * Public domain, by Fabian "ryg" Giesen */ - union + const fp32 f32infty = { 255U << 23 }; + const fp32 f16infty = { 31U << 23 }; + const fp32 magic = { 15U << 23 }; + const uint32_t sign_mask = 0x80000000U; + const uint32_t round_mask = ~0xFFFU; + + fp32 in; + uint16_t out; + + in.f = value; + + uint32_t sign = in.u & sign_mask; + in.u ^= sign; + + if (in.u >= f32infty.u) /* Inf or NaN (all exponent bits set) */ { - float val; - uint32_t valbits; - } conv; - - uint32_t f_exp, f_sig; - uint16_t h_sgn, h_exp, h_sig; - - conv.val = value; - - h_sgn = uint16_t((conv.valbits & 0x80000000U) >> 16); - f_exp = (conv.valbits & 0x7F800000U); - - /* Exponent overflow/NaN converts to signed inf/NaN */ - if (f_exp >= 0x47800000U) + /* NaN->sNaN and Inf->Inf */ + out = (in.u > f32infty.u) ? 0x7FFFU : 0x7C00U; + } + else /* (De)normalized number or zero */ { - if (f_exp == 0x7F800000U) + in.u &= round_mask; + in.f *= magic.f; + in.u -= round_mask; + if (in.u > f16infty.u) { - /* Inf or NaN */ - f_sig = (conv.valbits & 0x007FFFFFU); - if (f_sig != 0) - { - /* NaN - propagate the flag in the significand... */ - return uint16_t(h_sgn | 0x7FFFU); - } - else - { - /* signed inf */ - return uint16_t(h_sgn + 0x7C00U); - } - } - else - { - /* overflow to signed inf */ - return uint16_t(h_sgn + 0x7C00U); + in.u = f16infty.u; /* Clamp to signed infinity if overflowed */ } + + out = uint16_t(in.u >> 13); /* Take the bits! */ } - /* Exponent underflow converts to a subnormal half or signed zero */ - if (f_exp <= 0x38000000U) - { - /* - * Signed zeros, subnormal floats, and floats with small - * exponents all convert to signed zero halfs. - */ - if (f_exp < 0x33000000U) - { - return h_sgn; - } + out |= uint16_t(sign >> 16); - /* Make the subnormal significand */ - f_exp >>= 23; - f_sig = (0x00800000U + (conv.valbits & 0x007FFFFFU)); - f_sig >>= (113 - f_exp); - /* Handle rounding by adding 1 to the bit beyond half precision */ - f_sig += 0x00001000U; - - h_sig = uint16_t(f_sig >> 13); - - /* - * If the rounding causes a bit to spill into h_exp, it will - * increment h_exp from zero to one and h_sig will be zero. - * This is the correct result. - */ - return uint16_t(h_sgn + h_sig); - } - - /* Regular case with no overflow or underflow */ - h_exp = uint16_t((f_exp - 0x38000000U) >> 13); - - /* Handle rounding by adding 1 to the bit beyond half precision */ - f_sig = (conv.valbits & 0x007FFFFFU); - f_sig += 0x00001000U; - - h_sig = uint16_t(f_sig >> 13); - - /* - * If the rounding causes a bit to spill into h_exp, it will - * increment h_exp by one and h_sig will be zero. This is the - * correct result. h_exp may increment to 15, at greatest, in - * which case the result overflows to a signed inf. - */ - return uint16_t(h_sgn + h_exp + h_sig); + return out; } float IEEE754Converter::halfToNativeIeee(uint16_t value) { /* - * https://github.com/numpy/numpy/blob/master/numpy/core/src/npymath/halffloat.c - * BSD license + * https://gist.github.com/rygorous/2144712 + * Public domain, by Fabian "ryg" Giesen */ - union - { - float ret; - uint32_t retbits; - } conv; + const fp32 magic = { (254U - 15U) << 23 }; + const fp32 was_infnan = { (127U + 16U) << 23 }; + fp32 out; - uint16_t h_exp, h_sig; - uint32_t f_sgn, f_exp, f_sig; + out.u = (value & 0x7FFFU) << 13; /* exponent/mantissa bits */ + out.f *= magic.f; /* exponent adjust */ + if (out.f >= was_infnan.f) /* make sure Inf/NaN survive */ + { + out.u |= 255U << 23; + } + out.u |= (value & 0x8000U) << 16; /* sign bit */ - h_exp = value & 0x7C00U; - f_sgn = uint32_t(value & 0x8000U) << 16; - switch (h_exp) - { - case 0x0000U: /* 0 or subnormal */ - { - h_sig = (value & 0x03FFU); - if (h_sig == 0) - { - /* Signed zero */ - conv.retbits = f_sgn; - } - else - { - /* Subnormal */ - h_sig = uint16_t(h_sig << 1); - while ((h_sig & 0x0400U) == 0) - { - h_sig = uint16_t(h_sig << 1); - h_exp++; - } - f_exp = uint32_t(127 - 15 - h_exp) << 23; - f_sig = uint32_t(h_sig & 0x03FFU) << 13; - conv.retbits = f_sgn + f_exp + f_sig; - } - break; - } - case 0x7C00U: /* inf or NaN */ - { /* All-ones exponent and a copy of the significand */ - conv.retbits = f_sgn + 0x7F800000U + (uint32_t(value & 0x03FFU) << 13); - break; - } - default: /* normalized */ - { /* Just need to adjust the exponent and shift */ - conv.retbits = f_sgn + ((uint32_t(value & 0x7FFFU) + 0x1C000U) << 13); - break; - } - } - return conv.ret; + return out.f; } } +#endif // !UAVCAN_USE_EXTERNAL_FLOAT16_CONVERSION From 4ecdfd844e421d6f9d68f108f9207d1c180e8eff Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Mon, 24 Aug 2015 14:06:56 +0300 Subject: [PATCH 8/8] Minor style fix in IEEE754Converter; no changes to the logic --- libuavcan/src/marshal/uc_float_spec.cpp | 26 ++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/libuavcan/src/marshal/uc_float_spec.cpp b/libuavcan/src/marshal/uc_float_spec.cpp index 3254768e43..13a1a7ac62 100644 --- a/libuavcan/src/marshal/uc_float_spec.cpp +++ b/libuavcan/src/marshal/uc_float_spec.cpp @@ -6,14 +6,16 @@ #include #include -#if !UAVCAN_USE_EXTERNAL_FLOAT16_CONVERSION namespace uavcan { -typedef union + +#if !UAVCAN_USE_EXTERNAL_FLOAT16_CONVERSION + +union Fp32 { uint32_t u; float f; -} fp32; +}; /* * IEEE754Converter @@ -24,13 +26,13 @@ uint16_t IEEE754Converter::nativeIeeeToHalf(float value) * https://gist.github.com/rygorous/2156668 * Public domain, by Fabian "ryg" Giesen */ - const fp32 f32infty = { 255U << 23 }; - const fp32 f16infty = { 31U << 23 }; - const fp32 magic = { 15U << 23 }; + const Fp32 f32infty = { 255U << 23 }; + const Fp32 f16infty = { 31U << 23 }; + const Fp32 magic = { 15U << 23 }; const uint32_t sign_mask = 0x80000000U; const uint32_t round_mask = ~0xFFFU; - fp32 in; + Fp32 in; uint16_t out; in.f = value; @@ -67,9 +69,9 @@ float IEEE754Converter::halfToNativeIeee(uint16_t value) * https://gist.github.com/rygorous/2144712 * Public domain, by Fabian "ryg" Giesen */ - const fp32 magic = { (254U - 15U) << 23 }; - const fp32 was_infnan = { (127U + 16U) << 23 }; - fp32 out; + const Fp32 magic = { (254U - 15U) << 23 }; + const Fp32 was_infnan = { (127U + 16U) << 23 }; + Fp32 out; out.u = (value & 0x7FFFU) << 13; /* exponent/mantissa bits */ out.f *= magic.f; /* exponent adjust */ @@ -81,5 +83,7 @@ float IEEE754Converter::halfToNativeIeee(uint16_t value) return out.f; } -} + #endif // !UAVCAN_USE_EXTERNAL_FLOAT16_CONVERSION + +}