mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-29 01:23:34 +08:00
Merge pull request #60 from UAVCAN/bendyer-half-nostdlib
Use public domain half<->float routines which dependence on math functions in C stdlib
This commit is contained in:
@@ -143,6 +143,14 @@
|
|||||||
# define UAVCAN_USE_EXTERNAL_SNPRINTF 0
|
# define UAVCAN_USE_EXTERNAL_SNPRINTF 0
|
||||||
#endif
|
#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.
|
* Run time checks.
|
||||||
* Resolves to the standard assert() by default.
|
* Resolves to the standard assert() by default.
|
||||||
|
|||||||
@@ -36,12 +36,25 @@ struct NativeFloatSelector
|
|||||||
|
|
||||||
class UAVCAN_EXPORT IEEE754Converter
|
class UAVCAN_EXPORT IEEE754Converter
|
||||||
{
|
{
|
||||||
// TODO: Non-IEEE float support for float32 and float64
|
// TODO: Non-IEEE float support
|
||||||
static uint16_t nativeNonIeeeToHalf(float value);
|
|
||||||
static float halfToNativeNonIeee(uint16_t value);
|
static uint16_t nativeIeeeToHalf(float value);
|
||||||
|
static float halfToNativeIeee(uint16_t value);
|
||||||
|
|
||||||
IEEE754Converter();
|
IEEE754Converter();
|
||||||
|
|
||||||
|
template <unsigned BitLen>
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
|
||||||
|
StaticAssert<std::numeric_limits<typename NativeFloatSelector<BitLen>::Type>::is_iec559>::check();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
|
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
|
||||||
/// UAVCAN requires rounding to nearest for all float conversions
|
/// UAVCAN requires rounding to nearest for all float conversions
|
||||||
@@ -52,6 +65,7 @@ public:
|
|||||||
static typename IntegerSpec<BitLen, SignednessUnsigned, CastModeTruncate>::StorageType
|
static typename IntegerSpec<BitLen, SignednessUnsigned, CastModeTruncate>::StorageType
|
||||||
toIeee(typename NativeFloatSelector<BitLen>::Type value)
|
toIeee(typename NativeFloatSelector<BitLen>::Type value)
|
||||||
{
|
{
|
||||||
|
enforceIeee<BitLen>();
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
typename IntegerSpec<BitLen, SignednessUnsigned, CastModeTruncate>::StorageType i;
|
typename IntegerSpec<BitLen, SignednessUnsigned, CastModeTruncate>::StorageType i;
|
||||||
@@ -66,6 +80,7 @@ public:
|
|||||||
static typename NativeFloatSelector<BitLen>::Type
|
static typename NativeFloatSelector<BitLen>::Type
|
||||||
toNative(typename IntegerSpec<BitLen, SignednessUnsigned, CastModeTruncate>::StorageType value)
|
toNative(typename IntegerSpec<BitLen, SignednessUnsigned, CastModeTruncate>::StorageType value)
|
||||||
{
|
{
|
||||||
|
enforceIeee<BitLen>();
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
typename IntegerSpec<BitLen, SignednessUnsigned, CastModeTruncate>::StorageType i;
|
typename IntegerSpec<BitLen, SignednessUnsigned, CastModeTruncate>::StorageType i;
|
||||||
@@ -80,13 +95,13 @@ template <>
|
|||||||
inline typename IntegerSpec<16, SignednessUnsigned, CastModeTruncate>::StorageType
|
inline typename IntegerSpec<16, SignednessUnsigned, CastModeTruncate>::StorageType
|
||||||
IEEE754Converter::toIeee<16>(typename NativeFloatSelector<16>::Type value)
|
IEEE754Converter::toIeee<16>(typename NativeFloatSelector<16>::Type value)
|
||||||
{
|
{
|
||||||
return nativeNonIeeeToHalf(value);
|
return nativeIeeeToHalf(value);
|
||||||
}
|
}
|
||||||
template <>
|
template <>
|
||||||
inline typename NativeFloatSelector<16>::Type
|
inline typename NativeFloatSelector<16>::Type
|
||||||
IEEE754Converter::toNative<16>(typename IntegerSpec<16, SignednessUnsigned, CastModeTruncate>::StorageType value)
|
IEEE754Converter::toNative<16>(typename IntegerSpec<16, SignednessUnsigned, CastModeTruncate>::StorageType value)
|
||||||
{
|
{
|
||||||
return halfToNativeNonIeee(value);
|
return halfToNativeIeee(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,87 +6,84 @@
|
|||||||
#include <uavcan/build_config.hpp>
|
#include <uavcan/build_config.hpp>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#if !defined(UAVCAN_CPP_VERSION) || !defined(UAVCAN_CPP11)
|
|
||||||
# error UAVCAN_CPP_VERSION
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
|
|
||||||
# include <limits>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace uavcan
|
namespace uavcan
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#if !UAVCAN_USE_EXTERNAL_FLOAT16_CONVERSION
|
||||||
|
|
||||||
|
union Fp32
|
||||||
|
{
|
||||||
|
uint32_t u;
|
||||||
|
float f;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* IEEE754Converter
|
* IEEE754Converter
|
||||||
* Float16 conversion algorithm: http://half.sourceforge.net/ (MIT License)
|
|
||||||
*/
|
*/
|
||||||
uint16_t IEEE754Converter::nativeNonIeeeToHalf(float value)
|
uint16_t IEEE754Converter::nativeIeeeToHalf(float value)
|
||||||
{
|
{
|
||||||
uint16_t hbits = uint16_t(getSignBit(value) ? 0x8000U : 0);
|
/*
|
||||||
if (areFloatsExactlyEqual(value, 0.0F))
|
* 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 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) */
|
||||||
{
|
{
|
||||||
return hbits;
|
/* NaN->sNaN and Inf->Inf */
|
||||||
|
out = (in.u > f32infty.u) ? 0x7FFFU : 0x7C00U;
|
||||||
}
|
}
|
||||||
if (isNaN(value))
|
else /* (De)normalized number or zero */
|
||||||
{
|
{
|
||||||
return hbits | 0x7FFFU;
|
in.u &= round_mask;
|
||||||
|
in.f *= magic.f;
|
||||||
|
in.u -= round_mask;
|
||||||
|
if (in.u > f16infty.u)
|
||||||
|
{
|
||||||
|
in.u = f16infty.u; /* Clamp to signed infinity if overflowed */
|
||||||
|
}
|
||||||
|
|
||||||
|
out = uint16_t(in.u >> 13); /* Take the bits! */
|
||||||
}
|
}
|
||||||
if (isInfinity(value))
|
|
||||||
{
|
out |= uint16_t(sign >> 16);
|
||||||
return hbits | 0x7C00U;
|
|
||||||
}
|
return out;
|
||||||
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<int32_t>(value);
|
|
||||||
hbits = uint16_t(hbits | (uint32_t((ival < 0) ? (-ival) : ival) & 0x3FFU));
|
|
||||||
float diff = std::fabs(value - static_cast<float>(ival));
|
|
||||||
hbits = uint16_t(hbits + (diff >= 0.5F));
|
|
||||||
return hbits;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float IEEE754Converter::halfToNativeNonIeee(uint16_t value)
|
float IEEE754Converter::halfToNativeIeee(uint16_t value)
|
||||||
{
|
{
|
||||||
float out;
|
/*
|
||||||
unsigned abs = value & 0x7FFFU;
|
* https://gist.github.com/rygorous/2144712
|
||||||
if (abs > 0x7C00U)
|
* Public domain, by Fabian "ryg" Giesen
|
||||||
|
*/
|
||||||
|
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 */
|
||||||
|
if (out.f >= was_infnan.f) /* make sure Inf/NaN survive */
|
||||||
{
|
{
|
||||||
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
|
out.u |= 255U << 23;
|
||||||
out = std::numeric_limits<float>::has_quiet_NaN ? std::numeric_limits<float>::quiet_NaN() : 0.0F;
|
|
||||||
#else
|
|
||||||
out = nanf("");
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
else if (abs == 0x7C00U)
|
out.u |= (value & 0x8000U) << 16; /* sign bit */
|
||||||
{
|
|
||||||
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
|
return out.f;
|
||||||
out = std::numeric_limits<float>::has_infinity ?
|
|
||||||
std::numeric_limits<float>::infinity() : std::numeric_limits<float>::max();
|
|
||||||
#else
|
|
||||||
out = NumericTraits<float>::infinity();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else if (abs > 0x3FFU)
|
|
||||||
{
|
|
||||||
out = std::ldexp(static_cast<float>((value & 0x3FFU) | 0x400U), int(abs >> 10) - 25);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
out = std::ldexp(static_cast<float>(abs), -24);
|
|
||||||
}
|
|
||||||
return (value & 0x8000U) ? -out : out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // !UAVCAN_USE_EXTERNAL_FLOAT16_CONVERSION
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user