From 37bd23e4faad0f4318e17186ddf640df97157502 Mon Sep 17 00:00:00 2001 From: Ben Dyer Date: Mon, 24 Aug 2015 20:07:03 +1000 Subject: [PATCH] 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