Replace float<->half implementation with public domain code

This commit is contained in:
Ben Dyer
2015-08-24 20:07:03 +10:00
parent a7fe27a365
commit 37bd23e4fa
2 changed files with 59 additions and 127 deletions
@@ -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.
+51 -127
View File
@@ -6,156 +6,80 @@
#include <uavcan/build_config.hpp>
#include <cmath>
#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