JD2022-TU1/main/extern/gear4/gear_testing/math/floatingpoint.h

356 lines
11 KiB
C++

#pragma once
#ifndef _GEAR_TESTING__MATH__FLOATINGPOINT_H_
#define _GEAR_TESTING__MATH__FLOATINGPOINT_H_
#include <gear_testing/gear_testing.h>
#include <ealdef.h>
#include <assert.h>
#if ((G4_PLATFORM == G4_PLATFORM_LINUX) || (G4_PLATFORM == G4_PLATFORM_MAC) || (G4_PLATFORM == G4_PLATFORM_IOS))
#include <cstdlib>
#include <cmath>
#endif
#ifdef G4_PROD_DEFAULT_EPSILON_F32
static const float G4_TESTS_EPSILON_F32 = G4_PROD_DEFAULT_EPSILON_F32;
#else
static const float G4_TESTS_EPSILON_F32 = 0.000001f;
#endif
#ifdef G4_PROD_DEFAULT_EPSILON_F64
static const double G4_TESTS_EPSILON_F64 = G4_PROD_DEFAULT_EPSILON_F64;
#else
static const double G4_TESTS_EPSILON_F64 = 0.00000001;
#endif
#ifdef G4_PROD_DEFAULT_EPSILON_SIMD_F32
static const float G4_TESTS_EPSILON_SIMD_F32 = G4_PROD_DEFAULT_EPSILON_SIMD_F32;
#else
static const float G4_TESTS_EPSILON_SIMD_F32 = 0.000001f;
#endif
namespace G4
{
namespace Private
{
template <class T>
struct FloatingPointBaseTraits;
template <>
struct FloatingPointBaseTraits<double>
{
typedef double FloatType;
typedef eal_u64 UnsignedType;
typedef eal_s64 SignedType;
static const size_t s_BITS = sizeof(UnsignedType) * 8;
static const size_t s_EXPONENTBITS = 11;
static const size_t s_FRACTIONBITS = s_BITS - s_EXPONENTBITS - 1;
static G4_CONSTEXPR double GetTestsEpsilon() { return G4_TESTS_EPSILON_F64; }
};
template <>
struct FloatingPointBaseTraits<float>
{
typedef float FloatType;
typedef eal_u32 UnsignedType;
typedef eal_s32 SignedType;
static const size_t s_BITS = sizeof(UnsignedType) * 8;
static const size_t s_EXPONENTBITS = 8;
static const size_t s_FRACTIONBITS = s_BITS - s_EXPONENTBITS - 1;
static G4_CONSTEXPR float GetTestsEpsilon() { return G4_TESTS_EPSILON_F32; }
};
}
template <class T>
class FloatingPoint
{
public:
typedef Private::FloatingPointBaseTraits<T> BaseTraits;
typedef typename BaseTraits::FloatType FloatType;
typedef typename BaseTraits::UnsignedType UnsignedType;
typedef typename BaseTraits::SignedType SignedType;
static const size_t s_BITS = BaseTraits::s_BITS;
static const size_t s_EXPONENTBITS = BaseTraits::s_EXPONENTBITS;
static const size_t s_FRACTIONBITS = BaseTraits::s_FRACTIONBITS;
static const UnsignedType s_SIGNMASK = UnsignedType(1) << (s_BITS-1);
static const UnsignedType s_EXPONENTMASK = ((UnsignedType(1) << s_EXPONENTBITS) - 1) << (s_BITS - s_EXPONENTBITS - 1);
static const UnsignedType s_FRACTIONMASK = (UnsignedType(1) << s_FRACTIONBITS) - 1;
static const SignedType s_BIAS = (SignedType(1) << (s_EXPONENTBITS - 1)) - 1;
static const UnsignedType s_INFINITY = s_EXPONENTMASK;
static const UnsignedType s_NINFINITY = s_EXPONENTMASK | s_SIGNMASK;
static const UnsignedType s_NAN = s_EXPONENTMASK | UnsignedType(1);
explicit FloatingPoint(FloatType v = FloatType(0))
{
m_value.m_float = v;
}
explicit FloatingPoint(UnsignedType v)
{
m_value.m_unsigned = v;
}
explicit FloatingPoint(SignedType v)
{
if (v < 0)
{
m_value.m_unsigned = -v;
m_value.m_unsigned |= s_SIGNMASK;
}
else
m_value.m_unsigned = v;
}
UnsignedType GetUnsigned() const
{
return m_value.m_unsigned;
}
SignedType GetSigned() const
{
// Float used a bit-signed representation.
// The valid signed representation
UnsignedType u = GetUnsigned();
if (u & s_SIGNMASK)
return (~u) + 1;
return u;
}
FloatType GetReal() const
{
return m_value.m_float;
}
operator UnsignedType() const
{
return m_value.m_unsigned;
}
operator SignedType() const
{
return GetSigned();
}
operator FloatType() const
{
return m_value.m_float;
}
bool IsPositive() const
{
return (GetUnsigned() & s_SIGNMASK) == 0u;
}
bool IsNAN() const
{
UnsignedType v = GetUnsigned();
return
((v & s_EXPONENTMASK) == s_EXPONENTMASK)
&& ((v & s_FRACTIONMASK) != 0);
}
bool IsInfinite() const
{
UnsignedType v = GetUnsigned();
return
((v & s_EXPONENTMASK) == s_EXPONENTMASK)
&& ((v & s_FRACTIONMASK) == 0);
}
bool IsSubnominal() const
{
UnsignedType v = GetUnsigned();
return (v & s_EXPONENTMASK) == 0;
}
UnsignedType GetBiasedExponent() const
{
return (GetUnsigned() & s_EXPONENTMASK) >> s_FRACTIONBITS;
}
SignedType GetExponent() const
{
SignedType exp = GetBiasedExponent();
assert(exp <= 2*s_BIAS+1);
assert(exp >= 0);
return exp - s_BIAS;
}
void SetBiasedExponent(UnsignedType newExp)
{
m_value.m_unsigned &= ~s_EXPONENTMASK;
m_value.m_unsigned |= newExp << s_FRACTIONBITS;
}
void SetExponent(SignedType newExp)
{
assert(newExp + s_BIAS >= 0);
assert(newExp + s_BIAS <= static_cast<SignedType>(s_EXPONENTMASK >> s_FRACTIONBITS));
SetBiasedExponent(newExp + s_BIAS);
}
bool IsValid() const
{
// No infinite, no NAN
if (IsInfinite() || IsNAN())
{
return false;
}
return true;
}
/// Return true if v is atmost the maxULPth neighborhood of this.
/// Always return false if this or v is NAN.
/// Please, note that, by this definition, FLOAT_MAX is very near INFINITY.
/// maxULP stands for maximum Units in Last Position, one unit being the maximum represent precision
/// of this float for this magnitude. More explanation is available here:
/// http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
bool AlmostEquals(const FloatingPoint<T>& v, const SignedType maxULP = 4) const
{
assert(maxULP > 0);
if (IsNAN() || v.IsNAN()) return false;
return std::abs(v.GetSigned() - GetSigned()) < maxULP;
}
/// Return an approximate relative error for the specified maxULP.
static FloatType ULPToRelative(UnsignedType maxULP)
{
// 1.0f convert exactly in double, so no precautions need here.
static const UnsignedType One = FloatingPoint(1.0f).GetUnsigned();
return FloatingPoint(One | maxULP).GetReal()-FloatType(1.0f);
}
FloatType ULPToAbsolute(UnsignedType maxULP) const
{
return ULPToRelative(maxULP) * std::abs(GetReal());
}
static UnsignedType RelativeToULP(FloatType err)
{
return FloatingPoint(err).GetUnsigned() & s_FRACTIONMASK;
}
UnsignedType AbsoluteToULP(FloatType err) const
{
return RelativeToULP((std::abs(GetReal()) + err)/std::abs(GetReal()));
}
static FloatingPoint GetNAN()
{
return FloatingPoint(s_NAN);
}
static FloatingPoint GetInfinity()
{
return FloatingPoint(s_INFINITY);
}
static FloatingPoint GetNInfinity()
{
return FloatingPoint(s_NINFINITY);
}
static FloatType GetTestsEpsilon()
{
return BaseTraits::GetTestsEpsilon();
}
private:
union {
FloatType m_float;
UnsignedType m_unsigned;
} m_value;
};
template <class FType>
inline FloatingPoint<FType> AsFloatingPoint(FType v)
{
return FloatingPoint<FType>(v);
}
inline FloatingPoint<float> AsFloatingPoint(FloatingPoint<float> v)
{
return v;
}
inline FloatingPoint<double> AsFloatingPoint(FloatingPoint<double> v)
{
return v;
}
// Stream operations are not supported on SPU
#if !defined(__SPU__)
template <class T>
std::ostream& operator<<(std::ostream& os, const FloatingPoint<T>& v)
{
return os << v.GetReal() << " [0x" << std::hex << v.GetUnsigned() << std::dec << "]";
}
#endif
template <class T>
bool FPAlmostEquals(const FloatingPoint<T>& v1, const FloatingPoint<T>& v2,
const typename FloatingPoint<T>::UnsignedType maxULP)
{
const typename FloatingPoint<T>::UnsignedType realULP = maxULP*32/4;
if (v1.AlmostEquals(v2, realULP))
return true;
static const T minEpsilon = FloatingPoint<T>(1.0f).ULPToAbsolute(maxULP);
return std::abs(v1.GetReal() - v2.GetReal()) < minEpsilon;
};
template <typename FType>
inline typename FloatingPoint<FType>::UnsignedType
FPToBits(FType f)
{
FloatingPoint<FType> conv(f);
return conv.GetUnsigned();
}
// Stream operations are not supported on SPU
#if !defined(__SPU__)
template <typename FType>
inline std::ostream& PrintFloat(std::ostream& os, FType f)
{
if (f == f)
return os << f << " [" << std::hex << FPToBits(f) << std::dec << "]";
return os << "INV." << std::hex << FPToBits(f) << std::dec;
}
#endif
}
#if !defined(__SPU__)
#define EXPECT_FP_NEAR(T, V1, V2, ULP) EXPECT_PRED3(G4::FPAlmostEquals<T>, G4::FloatingPoint<T>(V1), G4::FloatingPoint<T>(V2), ULP)
#define EXPECT_FP_EQ(T, V1, V2) EXPECT_FP_NEAR(T, V1, V2, 4)
#else
namespace SPUTesting
{
struct FPAlmostEquals
{
template <typename T>
::testing::AssertionResult operator()(
const ::G4::FloatingPoint<T>& v1,
const ::G4::FloatingPoint<T>& v2,
const typename ::G4::FloatingPoint<T>::UnsignedType maxULP)
{
if(::G4::FPAlmostEquals<T>(v1, v2, maxULP))
return ::testing::AssertionSuccess();
return ::testing::AssertionFailure();
}
};
}
#define EXPECT_FP_NEAR(T, V1, V2, ULP) \
__EXPECT_CALL( \
SPUTesting::FPAlmostEquals(), \
G4::FloatingPoint<T>((V1)), \
G4::FloatingPoint<T>((V2)), \
(ULP))
#define EXPECT_FP_EQ(T, V1, V2) EXPECT_FP_NEAR(T, V1, V2, 4)
#endif // #if !defined(__SPU__)
#endif