/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/Assertions.h" #include "mozilla/FloatingPoint.h" #include using mozilla::ExponentComponent; using mozilla::FloatingPoint; using mozilla::FuzzyEqualsAdditive; using mozilla::FuzzyEqualsMultiplicative; using mozilla::IsFloat32Representable; using mozilla::IsNegative; using mozilla::IsNegativeZero; using mozilla::IsPositiveZero; using mozilla::NegativeInfinity; using mozilla::NumberEqualsInt32; using mozilla::NumberEqualsInt64; using mozilla::NumberIsInt32; using mozilla::NumberIsInt64; using mozilla::NumbersAreIdentical; using mozilla::PositiveInfinity; using mozilla::SpecificNaN; using mozilla::UnspecifiedNaN; using std::exp2; using std::exp2f; #define A(a) MOZ_RELEASE_ASSERT(a) template static void ShouldBeIdentical(T aD1, T aD2) { A(NumbersAreIdentical(aD1, aD2)); A(NumbersAreIdentical(aD2, aD1)); } template static void ShouldNotBeIdentical(T aD1, T aD2) { A(!NumbersAreIdentical(aD1, aD2)); A(!NumbersAreIdentical(aD2, aD1)); } static void TestDoublesAreIdentical() { ShouldBeIdentical(+0.0, +0.0); ShouldBeIdentical(-0.0, -0.0); ShouldNotBeIdentical(+0.0, -0.0); ShouldBeIdentical(1.0, 1.0); ShouldNotBeIdentical(-1.0, 1.0); ShouldBeIdentical(4294967295.0, 4294967295.0); ShouldNotBeIdentical(-4294967295.0, 4294967295.0); ShouldBeIdentical(4294967296.0, 4294967296.0); ShouldBeIdentical(4294967297.0, 4294967297.0); ShouldBeIdentical(1e300, 1e300); ShouldBeIdentical(PositiveInfinity(), PositiveInfinity()); ShouldBeIdentical(NegativeInfinity(), NegativeInfinity()); ShouldNotBeIdentical(PositiveInfinity(), NegativeInfinity()); ShouldNotBeIdentical(-0.0, NegativeInfinity()); ShouldNotBeIdentical(+0.0, NegativeInfinity()); ShouldNotBeIdentical(1e300, NegativeInfinity()); ShouldNotBeIdentical(3.141592654, NegativeInfinity()); ShouldBeIdentical(UnspecifiedNaN(), UnspecifiedNaN()); ShouldBeIdentical(-UnspecifiedNaN(), UnspecifiedNaN()); ShouldBeIdentical(UnspecifiedNaN(), -UnspecifiedNaN()); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 42)); ShouldBeIdentical(SpecificNaN(1, 17), SpecificNaN(1, 42)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(1, 42)); ShouldBeIdentical(SpecificNaN(1, 17), SpecificNaN(0, 42)); const uint64_t Mask = 0xfffffffffffffULL; for (unsigned i = 0; i < 52; i++) { for (unsigned j = 0; j < 52; j++) { for (unsigned sign = 0; i < 2; i++) { ShouldBeIdentical(SpecificNaN(0, 1ULL << i), SpecificNaN(sign, 1ULL << j)); ShouldBeIdentical(SpecificNaN(1, 1ULL << i), SpecificNaN(sign, 1ULL << j)); ShouldBeIdentical(SpecificNaN(0, Mask & ~(1ULL << i)), SpecificNaN(sign, Mask & ~(1ULL << j))); ShouldBeIdentical(SpecificNaN(1, Mask & ~(1ULL << i)), SpecificNaN(sign, Mask & ~(1ULL << j))); } } } ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x8000000000000ULL)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x4000000000000ULL)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x2000000000000ULL)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x1000000000000ULL)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x0800000000000ULL)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x0400000000000ULL)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x0200000000000ULL)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x0100000000000ULL)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x0080000000000ULL)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x0040000000000ULL)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x0020000000000ULL)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x0010000000000ULL)); ShouldBeIdentical(SpecificNaN(1, 17), SpecificNaN(0, 0xff0ffffffffffULL)); ShouldBeIdentical(SpecificNaN(1, 17), SpecificNaN(0, 0xfffffffffff0fULL)); ShouldNotBeIdentical(UnspecifiedNaN(), +0.0); ShouldNotBeIdentical(UnspecifiedNaN(), -0.0); ShouldNotBeIdentical(UnspecifiedNaN(), 1.0); ShouldNotBeIdentical(UnspecifiedNaN(), -1.0); ShouldNotBeIdentical(UnspecifiedNaN(), PositiveInfinity()); ShouldNotBeIdentical(UnspecifiedNaN(), NegativeInfinity()); } static void TestFloatsAreIdentical() { ShouldBeIdentical(+0.0f, +0.0f); ShouldBeIdentical(-0.0f, -0.0f); ShouldNotBeIdentical(+0.0f, -0.0f); ShouldBeIdentical(1.0f, 1.0f); ShouldNotBeIdentical(-1.0f, 1.0f); ShouldBeIdentical(8388607.0f, 8388607.0f); ShouldNotBeIdentical(-8388607.0f, 8388607.0f); ShouldBeIdentical(8388608.0f, 8388608.0f); ShouldBeIdentical(8388609.0f, 8388609.0f); ShouldBeIdentical(1e36f, 1e36f); ShouldBeIdentical(PositiveInfinity(), PositiveInfinity()); ShouldBeIdentical(NegativeInfinity(), NegativeInfinity()); ShouldNotBeIdentical(PositiveInfinity(), NegativeInfinity()); ShouldNotBeIdentical(-0.0f, NegativeInfinity()); ShouldNotBeIdentical(+0.0f, NegativeInfinity()); ShouldNotBeIdentical(1e36f, NegativeInfinity()); ShouldNotBeIdentical(3.141592654f, NegativeInfinity()); ShouldBeIdentical(UnspecifiedNaN(), UnspecifiedNaN()); ShouldBeIdentical(-UnspecifiedNaN(), UnspecifiedNaN()); ShouldBeIdentical(UnspecifiedNaN(), -UnspecifiedNaN()); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 42)); ShouldBeIdentical(SpecificNaN(1, 17), SpecificNaN(1, 42)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(1, 42)); ShouldBeIdentical(SpecificNaN(1, 17), SpecificNaN(0, 42)); const uint32_t Mask = 0x7fffffUL; for (unsigned i = 0; i < 23; i++) { for (unsigned j = 0; j < 23; j++) { for (unsigned sign = 0; i < 2; i++) { ShouldBeIdentical(SpecificNaN(0, 1UL << i), SpecificNaN(sign, 1UL << j)); ShouldBeIdentical(SpecificNaN(1, 1UL << i), SpecificNaN(sign, 1UL << j)); ShouldBeIdentical(SpecificNaN(0, Mask & ~(1UL << i)), SpecificNaN(sign, Mask & ~(1UL << j))); ShouldBeIdentical(SpecificNaN(1, Mask & ~(1UL << i)), SpecificNaN(sign, Mask & ~(1UL << j))); } } } ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x700000)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x400000)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x200000)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x100000)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x080000)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x040000)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x020000)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x010000)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x008000)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x004000)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x002000)); ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x001000)); ShouldBeIdentical(SpecificNaN(1, 17), SpecificNaN(0, 0x7f0fff)); ShouldBeIdentical(SpecificNaN(1, 17), SpecificNaN(0, 0x7fff0f)); ShouldNotBeIdentical(UnspecifiedNaN(), +0.0f); ShouldNotBeIdentical(UnspecifiedNaN(), -0.0f); ShouldNotBeIdentical(UnspecifiedNaN(), 1.0f); ShouldNotBeIdentical(UnspecifiedNaN(), -1.0f); ShouldNotBeIdentical(UnspecifiedNaN(), PositiveInfinity()); ShouldNotBeIdentical(UnspecifiedNaN(), NegativeInfinity()); } static void TestAreIdentical() { TestDoublesAreIdentical(); TestFloatsAreIdentical(); } static void TestDoubleExponentComponent() { A(ExponentComponent(0.0) == -int_fast16_t(FloatingPoint::kExponentBias)); A(ExponentComponent(-0.0) == -int_fast16_t(FloatingPoint::kExponentBias)); A(ExponentComponent(0.125) == -3); A(ExponentComponent(0.5) == -1); A(ExponentComponent(1.0) == 0); A(ExponentComponent(1.5) == 0); A(ExponentComponent(2.0) == 1); A(ExponentComponent(7.0) == 2); A(ExponentComponent(PositiveInfinity()) == FloatingPoint::kExponentBias + 1); A(ExponentComponent(NegativeInfinity()) == FloatingPoint::kExponentBias + 1); A(ExponentComponent(UnspecifiedNaN()) == FloatingPoint::kExponentBias + 1); } static void TestFloatExponentComponent() { A(ExponentComponent(0.0f) == -int_fast16_t(FloatingPoint::kExponentBias)); A(ExponentComponent(-0.0f) == -int_fast16_t(FloatingPoint::kExponentBias)); A(ExponentComponent(0.125f) == -3); A(ExponentComponent(0.5f) == -1); A(ExponentComponent(1.0f) == 0); A(ExponentComponent(1.5f) == 0); A(ExponentComponent(2.0f) == 1); A(ExponentComponent(7.0f) == 2); A(ExponentComponent(PositiveInfinity()) == FloatingPoint::kExponentBias + 1); A(ExponentComponent(NegativeInfinity()) == FloatingPoint::kExponentBias + 1); A(ExponentComponent(UnspecifiedNaN()) == FloatingPoint::kExponentBias + 1); } static void TestExponentComponent() { TestDoubleExponentComponent(); TestFloatExponentComponent(); } // Used to test Number{Is,Equals}{Int32,Int64} for -0.0, the only case where // NumberEquals* and NumberIs* aren't equivalent. template static void TestEqualsIsForNegativeZero() { T negZero = T(-0.0); int32_t i32; A(!NumberIsInt32(negZero, &i32)); A(NumberEqualsInt32(negZero, &i32)); A(i32 == 0); int64_t i64; A(!NumberIsInt64(negZero, &i64)); A(NumberEqualsInt64(negZero, &i64)); A(i64 == 0); } // Used to test Number{Is,Equals}{Int32,Int64} for int32 values. template static void TestEqualsIsForInt32(T aVal) { int32_t i32; A(NumberIsInt32(aVal, &i32)); MOZ_ASSERT(i32 == aVal); A(NumberEqualsInt32(aVal, &i32)); MOZ_ASSERT(i32 == aVal); int64_t i64; A(NumberIsInt64(aVal, &i64)); MOZ_ASSERT(i64 == aVal); A(NumberEqualsInt64(aVal, &i64)); MOZ_ASSERT(i64 == aVal); }; // Used to test Number{Is,Equals}{Int32,Int64} for values that fit in int64 but // not int32. template static void TestEqualsIsForInt64(T aVal) { int32_t i32; A(!NumberIsInt32(aVal, &i32)); A(!NumberEqualsInt32(aVal, &i32)); int64_t i64; A(NumberIsInt64(aVal, &i64)); MOZ_ASSERT(i64 == aVal); A(NumberEqualsInt64(aVal, &i64)); MOZ_ASSERT(i64 == aVal); }; // Used to test Number{Is,Equals}{Int32,Int64} for values that aren't equal to // any int32 or int64. template static void TestEqualsIsForNonInteger(T aVal) { int32_t i32; A(!NumberIsInt32(aVal, &i32)); A(!NumberEqualsInt32(aVal, &i32)); int64_t i64; A(!NumberIsInt64(aVal, &i64)); A(!NumberEqualsInt64(aVal, &i64)); }; static void TestDoublesPredicates() { A(std::isnan(UnspecifiedNaN())); A(std::isnan(SpecificNaN(1, 17))); ; A(std::isnan(SpecificNaN(0, 0xfffffffffff0fULL))); A(!std::isnan(PositiveInfinity())); A(!std::isnan(NegativeInfinity())); A(std::isinf(PositiveInfinity())); A(std::isinf(NegativeInfinity())); A(!std::isinf(UnspecifiedNaN())); A(!std::isfinite(PositiveInfinity())); A(!std::isfinite(NegativeInfinity())); A(!std::isfinite(UnspecifiedNaN())); A(!IsNegative(PositiveInfinity())); A(IsNegative(NegativeInfinity())); A(IsNegative(-0.0)); A(!IsNegative(0.0)); A(IsNegative(-1.0)); A(!IsNegative(1.0)); A(!IsNegativeZero(PositiveInfinity())); A(!IsNegativeZero(NegativeInfinity())); A(!IsNegativeZero(SpecificNaN(1, 17))); ; A(!IsNegativeZero(SpecificNaN(1, 0xfffffffffff0fULL))); A(!IsNegativeZero(SpecificNaN(0, 17))); ; A(!IsNegativeZero(SpecificNaN(0, 0xfffffffffff0fULL))); A(!IsNegativeZero(UnspecifiedNaN())); A(IsNegativeZero(-0.0)); A(!IsNegativeZero(0.0)); A(!IsNegativeZero(-1.0)); A(!IsNegativeZero(1.0)); // Edge case: negative zero. TestEqualsIsForNegativeZero(); // Int32 values. auto testInt32 = TestEqualsIsForInt32; testInt32(0.0); testInt32(1.0); testInt32(INT32_MIN); testInt32(INT32_MAX); // Int64 values that don't fit in int32. auto testInt64 = TestEqualsIsForInt64; testInt64(2147483648); testInt64(2147483649); testInt64(-2147483649); testInt64(INT64_MIN); // Note: INT64_MAX can't be represented exactly as double. Use a large double // very close to it. testInt64(9223372036854772000.0); constexpr double MinSafeInteger = -9007199254740991.0; constexpr double MaxSafeInteger = 9007199254740991.0; testInt64(MinSafeInteger); testInt64(MaxSafeInteger); // Doubles that aren't equal to any int32 or int64. auto testNonInteger = TestEqualsIsForNonInteger; testNonInteger(NegativeInfinity()); testNonInteger(PositiveInfinity()); testNonInteger(UnspecifiedNaN()); testNonInteger(-double(1ULL << 52) + 0.5); testNonInteger(double(1ULL << 52) - 0.5); testNonInteger(double(INT32_MAX) + 0.1); testNonInteger(double(INT32_MIN) - 0.1); testNonInteger(0.5); testNonInteger(-0.0001); testNonInteger(-9223372036854778000.0); testNonInteger(9223372036854776000.0); // Sanity-check that the IEEE-754 double-precision-derived literals used in // testing here work as we intend them to. A(exp2(-1075.0) == 0.0); A(exp2(-1074.0) != 0.0); testNonInteger(exp2(-1074.0)); testNonInteger(2 * exp2(-1074.0)); A(1.0 - exp2(-54.0) == 1.0); A(1.0 - exp2(-53.0) != 1.0); testNonInteger(1.0 - exp2(-53.0)); testNonInteger(1.0 - exp2(-52.0)); A(1.0 + exp2(-53.0) == 1.0f); A(1.0 + exp2(-52.0) != 1.0f); testNonInteger(1.0 + exp2(-52.0)); } static void TestFloatsPredicates() { A(std::isnan(UnspecifiedNaN())); A(std::isnan(SpecificNaN(1, 17))); ; A(std::isnan(SpecificNaN(0, 0x7fff0fUL))); A(!std::isnan(PositiveInfinity())); A(!std::isnan(NegativeInfinity())); A(std::isinf(PositiveInfinity())); A(std::isinf(NegativeInfinity())); A(!std::isinf(UnspecifiedNaN())); A(!std::isfinite(PositiveInfinity())); A(!std::isfinite(NegativeInfinity())); A(!std::isfinite(UnspecifiedNaN())); A(!IsNegative(PositiveInfinity())); A(IsNegative(NegativeInfinity())); A(IsNegative(-0.0f)); A(!IsNegative(0.0f)); A(IsNegative(-1.0f)); A(!IsNegative(1.0f)); A(!IsNegativeZero(PositiveInfinity())); A(!IsNegativeZero(NegativeInfinity())); A(!IsNegativeZero(SpecificNaN(1, 17))); ; A(!IsNegativeZero(SpecificNaN(1, 0x7fff0fUL))); A(!IsNegativeZero(SpecificNaN(0, 17))); ; A(!IsNegativeZero(SpecificNaN(0, 0x7fff0fUL))); A(!IsNegativeZero(UnspecifiedNaN())); A(IsNegativeZero(-0.0f)); A(!IsNegativeZero(0.0f)); A(!IsNegativeZero(-1.0f)); A(!IsNegativeZero(1.0f)); A(!IsPositiveZero(PositiveInfinity())); A(!IsPositiveZero(NegativeInfinity())); A(!IsPositiveZero(SpecificNaN(1, 17))); ; A(!IsPositiveZero(SpecificNaN(1, 0x7fff0fUL))); A(!IsPositiveZero(SpecificNaN(0, 17))); ; A(!IsPositiveZero(SpecificNaN(0, 0x7fff0fUL))); A(!IsPositiveZero(UnspecifiedNaN())); A(IsPositiveZero(0.0f)); A(!IsPositiveZero(-0.0f)); A(!IsPositiveZero(-1.0f)); A(!IsPositiveZero(1.0f)); // Edge case: negative zero. TestEqualsIsForNegativeZero(); // Int32 values. auto testInt32 = TestEqualsIsForInt32; testInt32(0.0f); testInt32(1.0f); testInt32(INT32_MIN); testInt32(float(2147483648 - 128)); // max int32_t fitting in float const int32_t BIG = 2097151; testInt32(BIG); // Int64 values that don't fit in int32. auto testInt64 = TestEqualsIsForInt64; testInt64(INT64_MIN); testInt64(9007199254740992.0f); testInt64(-float(2147483648) - 256); testInt64(float(2147483648)); testInt64(float(2147483648) + 256); // Floats that aren't equal to any int32 or int64. auto testNonInteger = TestEqualsIsForNonInteger; testNonInteger(NegativeInfinity()); testNonInteger(PositiveInfinity()); testNonInteger(UnspecifiedNaN()); testNonInteger(0.5f); testNonInteger(1.5f); testNonInteger(-0.0001f); testNonInteger(-19223373116872850000.0f); testNonInteger(19223373116872850000.0f); testNonInteger(float(BIG) + 0.1f); A(powf(2.0f, -150.0f) == 0.0f); A(powf(2.0f, -149.0f) != 0.0f); testNonInteger(powf(2.0f, -149.0f)); testNonInteger(2 * powf(2.0f, -149.0f)); A(1.0f - powf(2.0f, -25.0f) == 1.0f); A(1.0f - powf(2.0f, -24.0f) != 1.0f); testNonInteger(1.0f - powf(2.0f, -24.0f)); testNonInteger(1.0f - powf(2.0f, -23.0f)); A(1.0f + powf(2.0f, -24.0f) == 1.0f); A(1.0f + powf(2.0f, -23.0f) != 1.0f); testNonInteger(1.0f + powf(2.0f, -23.0f)); } static void TestPredicates() { TestFloatsPredicates(); TestDoublesPredicates(); } static void TestFloatsAreApproximatelyEqual() { float epsilon = mozilla::detail::FuzzyEqualsEpsilon::value(); float lessThanEpsilon = epsilon / 2.0f; float moreThanEpsilon = epsilon * 2.0f; // Additive tests using the default epsilon // ... around 1.0 A(FuzzyEqualsAdditive(1.0f, 1.0f + lessThanEpsilon)); A(FuzzyEqualsAdditive(1.0f, 1.0f - lessThanEpsilon)); A(FuzzyEqualsAdditive(1.0f, 1.0f + epsilon)); A(FuzzyEqualsAdditive(1.0f, 1.0f - epsilon)); A(!FuzzyEqualsAdditive(1.0f, 1.0f + moreThanEpsilon)); A(!FuzzyEqualsAdditive(1.0f, 1.0f - moreThanEpsilon)); // ... around 1.0e2 (this is near the upper bound of the range where // adding moreThanEpsilon will still be representable and return false) A(FuzzyEqualsAdditive(1.0e2f, 1.0e2f + lessThanEpsilon)); A(FuzzyEqualsAdditive(1.0e2f, 1.0e2f + epsilon)); A(!FuzzyEqualsAdditive(1.0e2f, 1.0e2f + moreThanEpsilon)); // ... around 1.0e-10 A(FuzzyEqualsAdditive(1.0e-10f, 1.0e-10f + lessThanEpsilon)); A(FuzzyEqualsAdditive(1.0e-10f, 1.0e-10f + epsilon)); A(!FuzzyEqualsAdditive(1.0e-10f, 1.0e-10f + moreThanEpsilon)); // ... straddling 0 A(FuzzyEqualsAdditive(1.0e-6f, -1.0e-6f)); A(!FuzzyEqualsAdditive(1.0e-5f, -1.0e-5f)); // Using a small epsilon A(FuzzyEqualsAdditive(1.0e-5f, 1.0e-5f + 1.0e-10f, 1.0e-9f)); A(!FuzzyEqualsAdditive(1.0e-5f, 1.0e-5f + 1.0e-10f, 1.0e-11f)); // Using a big epsilon A(FuzzyEqualsAdditive(1.0e20f, 1.0e20f + 1.0e15f, 1.0e16f)); A(!FuzzyEqualsAdditive(1.0e20f, 1.0e20f + 1.0e15f, 1.0e14f)); // Multiplicative tests using the default epsilon // ... around 1.0 A(FuzzyEqualsMultiplicative(1.0f, 1.0f + lessThanEpsilon)); A(FuzzyEqualsMultiplicative(1.0f, 1.0f - lessThanEpsilon)); A(FuzzyEqualsMultiplicative(1.0f, 1.0f + epsilon)); A(!FuzzyEqualsMultiplicative(1.0f, 1.0f - epsilon)); A(!FuzzyEqualsMultiplicative(1.0f, 1.0f + moreThanEpsilon)); A(!FuzzyEqualsMultiplicative(1.0f, 1.0f - moreThanEpsilon)); // ... around 1.0e10 A(FuzzyEqualsMultiplicative(1.0e10f, 1.0e10f + (lessThanEpsilon * 1.0e10f))); A(!FuzzyEqualsMultiplicative(1.0e10f, 1.0e10f + (moreThanEpsilon * 1.0e10f))); // ... around 1.0e-10 A(FuzzyEqualsMultiplicative(1.0e-10f, 1.0e-10f + (lessThanEpsilon * 1.0e-10f))); A(!FuzzyEqualsMultiplicative(1.0e-10f, 1.0e-10f + (moreThanEpsilon * 1.0e-10f))); // ... straddling 0 A(!FuzzyEqualsMultiplicative(1.0e-6f, -1.0e-6f)); A(FuzzyEqualsMultiplicative(1.0e-6f, -1.0e-6f, 1.0e2f)); // Using a small epsilon A(FuzzyEqualsMultiplicative(1.0e-5f, 1.0e-5f + 1.0e-10f, 1.0e-4f)); A(!FuzzyEqualsMultiplicative(1.0e-5f, 1.0e-5f + 1.0e-10f, 1.0e-5f)); // Using a big epsilon A(FuzzyEqualsMultiplicative(1.0f, 2.0f, 1.0f)); A(!FuzzyEqualsMultiplicative(1.0f, 2.0f, 0.1f)); // "real world case" float oneThird = 10.0f / 3.0f; A(FuzzyEqualsAdditive(10.0f, 3.0f * oneThird)); A(FuzzyEqualsMultiplicative(10.0f, 3.0f * oneThird)); // NaN check A(!FuzzyEqualsAdditive(SpecificNaN(1, 1), SpecificNaN(1, 1))); A(!FuzzyEqualsAdditive(SpecificNaN(1, 2), SpecificNaN(0, 8))); A(!FuzzyEqualsMultiplicative(SpecificNaN(1, 1), SpecificNaN(1, 1))); A(!FuzzyEqualsMultiplicative(SpecificNaN(1, 2), SpecificNaN(0, 200))); } static void TestDoublesAreApproximatelyEqual() { double epsilon = mozilla::detail::FuzzyEqualsEpsilon::value(); double lessThanEpsilon = epsilon / 2.0; double moreThanEpsilon = epsilon * 2.0; // Additive tests using the default epsilon // ... around 1.0 A(FuzzyEqualsAdditive(1.0, 1.0 + lessThanEpsilon)); A(FuzzyEqualsAdditive(1.0, 1.0 - lessThanEpsilon)); A(FuzzyEqualsAdditive(1.0, 1.0 + epsilon)); A(FuzzyEqualsAdditive(1.0, 1.0 - epsilon)); A(!FuzzyEqualsAdditive(1.0, 1.0 + moreThanEpsilon)); A(!FuzzyEqualsAdditive(1.0, 1.0 - moreThanEpsilon)); // ... around 1.0e4 (this is near the upper bound of the range where // adding moreThanEpsilon will still be representable and return false) A(FuzzyEqualsAdditive(1.0e4, 1.0e4 + lessThanEpsilon)); A(FuzzyEqualsAdditive(1.0e4, 1.0e4 + epsilon)); A(!FuzzyEqualsAdditive(1.0e4, 1.0e4 + moreThanEpsilon)); // ... around 1.0e-25 A(FuzzyEqualsAdditive(1.0e-25, 1.0e-25 + lessThanEpsilon)); A(FuzzyEqualsAdditive(1.0e-25, 1.0e-25 + epsilon)); A(!FuzzyEqualsAdditive(1.0e-25, 1.0e-25 + moreThanEpsilon)); // ... straddling 0 A(FuzzyEqualsAdditive(1.0e-13, -1.0e-13)); A(!FuzzyEqualsAdditive(1.0e-12, -1.0e-12)); // Using a small epsilon A(FuzzyEqualsAdditive(1.0e-15, 1.0e-15 + 1.0e-30, 1.0e-29)); A(!FuzzyEqualsAdditive(1.0e-15, 1.0e-15 + 1.0e-30, 1.0e-31)); // Using a big epsilon A(FuzzyEqualsAdditive(1.0e40, 1.0e40 + 1.0e25, 1.0e26)); A(!FuzzyEqualsAdditive(1.0e40, 1.0e40 + 1.0e25, 1.0e24)); // Multiplicative tests using the default epsilon // ... around 1.0 A(FuzzyEqualsMultiplicative(1.0, 1.0 + lessThanEpsilon)); A(FuzzyEqualsMultiplicative(1.0, 1.0 - lessThanEpsilon)); A(FuzzyEqualsMultiplicative(1.0, 1.0 + epsilon)); A(!FuzzyEqualsMultiplicative(1.0, 1.0 - epsilon)); A(!FuzzyEqualsMultiplicative(1.0, 1.0 + moreThanEpsilon)); A(!FuzzyEqualsMultiplicative(1.0, 1.0 - moreThanEpsilon)); // ... around 1.0e30 A(FuzzyEqualsMultiplicative(1.0e30, 1.0e30 + (lessThanEpsilon * 1.0e30))); A(!FuzzyEqualsMultiplicative(1.0e30, 1.0e30 + (moreThanEpsilon * 1.0e30))); // ... around 1.0e-30 A(FuzzyEqualsMultiplicative(1.0e-30, 1.0e-30 + (lessThanEpsilon * 1.0e-30))); A(!FuzzyEqualsMultiplicative(1.0e-30, 1.0e-30 + (moreThanEpsilon * 1.0e-30))); // ... straddling 0 A(!FuzzyEqualsMultiplicative(1.0e-6, -1.0e-6)); A(FuzzyEqualsMultiplicative(1.0e-6, -1.0e-6, 1.0e2)); // Using a small epsilon A(FuzzyEqualsMultiplicative(1.0e-15, 1.0e-15 + 1.0e-30, 1.0e-15)); A(!FuzzyEqualsMultiplicative(1.0e-15, 1.0e-15 + 1.0e-30, 1.0e-16)); // Using a big epsilon A(FuzzyEqualsMultiplicative(1.0e40, 2.0e40, 1.0)); A(!FuzzyEqualsMultiplicative(1.0e40, 2.0e40, 0.1)); // "real world case" double oneThird = 10.0 / 3.0; A(FuzzyEqualsAdditive(10.0, 3.0 * oneThird)); A(FuzzyEqualsMultiplicative(10.0, 3.0 * oneThird)); // NaN check A(!FuzzyEqualsAdditive(SpecificNaN(1, 1), SpecificNaN(1, 1))); A(!FuzzyEqualsAdditive(SpecificNaN(1, 2), SpecificNaN(0, 8))); A(!FuzzyEqualsMultiplicative(SpecificNaN(1, 1), SpecificNaN(1, 1))); A(!FuzzyEqualsMultiplicative(SpecificNaN(1, 2), SpecificNaN(0, 200))); } static void TestAreApproximatelyEqual() { TestFloatsAreApproximatelyEqual(); TestDoublesAreApproximatelyEqual(); } static void TestIsFloat32Representable() { // Zeroes are representable. A(IsFloat32Representable(+0.0)); A(IsFloat32Representable(-0.0)); // NaN and infinities are representable. A(IsFloat32Representable(UnspecifiedNaN())); A(IsFloat32Representable(SpecificNaN(0, 1))); A(IsFloat32Representable(SpecificNaN(0, 71389))); A(IsFloat32Representable(SpecificNaN(0, (uint64_t(1) << 52) - 2))); A(IsFloat32Representable(SpecificNaN(1, 1))); A(IsFloat32Representable(SpecificNaN(1, 71389))); A(IsFloat32Representable(SpecificNaN(1, (uint64_t(1) << 52) - 2))); A(IsFloat32Representable(PositiveInfinity())); A(IsFloat32Representable(NegativeInfinity())); // Sanity-check that the IEEE-754 double-precision-derived literals used in // testing here work as we intend them to. A(exp2(-1075.0) == 0.0); A(exp2(-1074.0) != 0.0); for (double littleExp = -1074.0; littleExp < -149.0; littleExp++) { // Powers of two representable as doubles but not as floats aren't // representable. A(!IsFloat32Representable(exp2(littleExp))); } // Sanity-check that the IEEE-754 single-precision-derived literals used in // testing here work as we intend them to. A(exp2f(-150.0f) == 0.0); A(exp2f(-149.0f) != 0.0); // Exact powers of two within the available range are representable. for (double exponent = -149.0; exponent < 128.0; exponent++) { A(IsFloat32Representable(exp2(exponent))); } // Powers of two above the available range aren't representable. for (double bigExp = 128.0; bigExp < 1024.0; bigExp++) { A(!IsFloat32Representable(exp2(bigExp))); } // Various denormal (i.e. super-small) doubles with MSB and LSB as far apart // as possible are representable (but taken one bit further apart are not // representable). // // Note that the final iteration tests non-denormal with exponent field // containing (biased) 1, as |oneTooSmall| and |widestPossible| happen still // to be correct for that exponent due to the extra bit of precision in the // implicit-one bit. double oneTooSmall = exp2(-150.0); for (double denormExp = -149.0; denormExp < 1 - double(FloatingPoint::kExponentBias) + 1; denormExp++) { double baseDenorm = exp2(denormExp); double tooWide = baseDenorm + oneTooSmall; A(!IsFloat32Representable(tooWide)); double widestPossible = baseDenorm; if (oneTooSmall * 2.0 != baseDenorm) { widestPossible += oneTooSmall * 2.0; } A(IsFloat32Representable(widestPossible)); } // Finally, check certain interesting/special values for basic sanity. A(!IsFloat32Representable(2147483647.0)); A(!IsFloat32Representable(-2147483647.0)); } #undef A int main() { TestAreIdentical(); TestExponentComponent(); TestPredicates(); TestAreApproximatelyEqual(); TestIsFloat32Representable(); return 0; }