#pragma once #ifndef _GEAR_TESTING__MATH__FLOATINGPOINT_H_ #define _GEAR_TESTING__MATH__FLOATINGPOINT_H_ #include #include #include #if ((G4_PLATFORM == G4_PLATFORM_LINUX) || (G4_PLATFORM == G4_PLATFORM_MAC) || (G4_PLATFORM == G4_PLATFORM_IOS)) #include #include #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 struct FloatingPointBaseTraits; template <> struct FloatingPointBaseTraits { 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 { 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 FloatingPoint { public: typedef Private::FloatingPointBaseTraits 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(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& 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 inline FloatingPoint AsFloatingPoint(FType v) { return FloatingPoint(v); } inline FloatingPoint AsFloatingPoint(FloatingPoint v) { return v; } inline FloatingPoint AsFloatingPoint(FloatingPoint v) { return v; } // Stream operations are not supported on SPU #if !defined(__SPU__) template std::ostream& operator<<(std::ostream& os, const FloatingPoint& v) { return os << v.GetReal() << " [0x" << std::hex << v.GetUnsigned() << std::dec << "]"; } #endif template bool FPAlmostEquals(const FloatingPoint& v1, const FloatingPoint& v2, const typename FloatingPoint::UnsignedType maxULP) { const typename FloatingPoint::UnsignedType realULP = maxULP*32/4; if (v1.AlmostEquals(v2, realULP)) return true; static const T minEpsilon = FloatingPoint(1.0f).ULPToAbsolute(maxULP); return std::abs(v1.GetReal() - v2.GetReal()) < minEpsilon; }; template inline typename FloatingPoint::UnsignedType FPToBits(FType f) { FloatingPoint conv(f); return conv.GetUnsigned(); } // Stream operations are not supported on SPU #if !defined(__SPU__) template 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, G4::FloatingPoint(V1), G4::FloatingPoint(V2), ULP) #define EXPECT_FP_EQ(T, V1, V2) EXPECT_FP_NEAR(T, V1, V2, 4) #else namespace SPUTesting { struct FPAlmostEquals { template ::testing::AssertionResult operator()( const ::G4::FloatingPoint& v1, const ::G4::FloatingPoint& v2, const typename ::G4::FloatingPoint::UnsignedType maxULP) { if(::G4::FPAlmostEquals(v1, v2, maxULP)) return ::testing::AssertionSuccess(); return ::testing::AssertionFailure(); } }; } #define EXPECT_FP_NEAR(T, V1, V2, ULP) \ __EXPECT_CALL( \ SPUTesting::FPAlmostEquals(), \ G4::FloatingPoint((V1)), \ G4::FloatingPoint((V2)), \ (ULP)) #define EXPECT_FP_EQ(T, V1, V2) EXPECT_FP_NEAR(T, V1, V2, 4) #endif // #if !defined(__SPU__) #endif