summaryrefslogtreecommitdiffstats
path: root/mfbt/tests/TestFloatingPoint.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mfbt/tests/TestFloatingPoint.cpp')
-rw-r--r--mfbt/tests/TestFloatingPoint.cpp730
1 files changed, 730 insertions, 0 deletions
diff --git a/mfbt/tests/TestFloatingPoint.cpp b/mfbt/tests/TestFloatingPoint.cpp
new file mode 100644
index 0000000000..44918cd1c5
--- /dev/null
+++ b/mfbt/tests/TestFloatingPoint.cpp
@@ -0,0 +1,730 @@
+/* -*- 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 <math.h>
+
+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 <typename T>
+static void ShouldBeIdentical(T aD1, T aD2) {
+ A(NumbersAreIdentical(aD1, aD2));
+ A(NumbersAreIdentical(aD2, aD1));
+}
+
+template <typename T>
+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<double>(), PositiveInfinity<double>());
+ ShouldBeIdentical(NegativeInfinity<double>(), NegativeInfinity<double>());
+ ShouldNotBeIdentical(PositiveInfinity<double>(), NegativeInfinity<double>());
+
+ ShouldNotBeIdentical(-0.0, NegativeInfinity<double>());
+ ShouldNotBeIdentical(+0.0, NegativeInfinity<double>());
+ ShouldNotBeIdentical(1e300, NegativeInfinity<double>());
+ ShouldNotBeIdentical(3.141592654, NegativeInfinity<double>());
+
+ ShouldBeIdentical(UnspecifiedNaN<double>(), UnspecifiedNaN<double>());
+ ShouldBeIdentical(-UnspecifiedNaN<double>(), UnspecifiedNaN<double>());
+ ShouldBeIdentical(UnspecifiedNaN<double>(), -UnspecifiedNaN<double>());
+
+ ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(0, 42));
+ ShouldBeIdentical(SpecificNaN<double>(1, 17), SpecificNaN<double>(1, 42));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(1, 42));
+ ShouldBeIdentical(SpecificNaN<double>(1, 17), SpecificNaN<double>(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<double>(0, 1ULL << i),
+ SpecificNaN<double>(sign, 1ULL << j));
+ ShouldBeIdentical(SpecificNaN<double>(1, 1ULL << i),
+ SpecificNaN<double>(sign, 1ULL << j));
+
+ ShouldBeIdentical(SpecificNaN<double>(0, Mask & ~(1ULL << i)),
+ SpecificNaN<double>(sign, Mask & ~(1ULL << j)));
+ ShouldBeIdentical(SpecificNaN<double>(1, Mask & ~(1ULL << i)),
+ SpecificNaN<double>(sign, Mask & ~(1ULL << j)));
+ }
+ }
+ }
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x8000000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x4000000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x2000000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x1000000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x0800000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x0400000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x0200000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x0100000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x0080000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x0040000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x0020000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x0010000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(1, 17),
+ SpecificNaN<double>(0, 0xff0ffffffffffULL));
+ ShouldBeIdentical(SpecificNaN<double>(1, 17),
+ SpecificNaN<double>(0, 0xfffffffffff0fULL));
+
+ ShouldNotBeIdentical(UnspecifiedNaN<double>(), +0.0);
+ ShouldNotBeIdentical(UnspecifiedNaN<double>(), -0.0);
+ ShouldNotBeIdentical(UnspecifiedNaN<double>(), 1.0);
+ ShouldNotBeIdentical(UnspecifiedNaN<double>(), -1.0);
+ ShouldNotBeIdentical(UnspecifiedNaN<double>(), PositiveInfinity<double>());
+ ShouldNotBeIdentical(UnspecifiedNaN<double>(), NegativeInfinity<double>());
+}
+
+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<float>(), PositiveInfinity<float>());
+ ShouldBeIdentical(NegativeInfinity<float>(), NegativeInfinity<float>());
+ ShouldNotBeIdentical(PositiveInfinity<float>(), NegativeInfinity<float>());
+
+ ShouldNotBeIdentical(-0.0f, NegativeInfinity<float>());
+ ShouldNotBeIdentical(+0.0f, NegativeInfinity<float>());
+ ShouldNotBeIdentical(1e36f, NegativeInfinity<float>());
+ ShouldNotBeIdentical(3.141592654f, NegativeInfinity<float>());
+
+ ShouldBeIdentical(UnspecifiedNaN<float>(), UnspecifiedNaN<float>());
+ ShouldBeIdentical(-UnspecifiedNaN<float>(), UnspecifiedNaN<float>());
+ ShouldBeIdentical(UnspecifiedNaN<float>(), -UnspecifiedNaN<float>());
+
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 42));
+ ShouldBeIdentical(SpecificNaN<float>(1, 17), SpecificNaN<float>(1, 42));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(1, 42));
+ ShouldBeIdentical(SpecificNaN<float>(1, 17), SpecificNaN<float>(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<float>(0, 1UL << i),
+ SpecificNaN<float>(sign, 1UL << j));
+ ShouldBeIdentical(SpecificNaN<float>(1, 1UL << i),
+ SpecificNaN<float>(sign, 1UL << j));
+
+ ShouldBeIdentical(SpecificNaN<float>(0, Mask & ~(1UL << i)),
+ SpecificNaN<float>(sign, Mask & ~(1UL << j)));
+ ShouldBeIdentical(SpecificNaN<float>(1, Mask & ~(1UL << i)),
+ SpecificNaN<float>(sign, Mask & ~(1UL << j)));
+ }
+ }
+ }
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x700000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x400000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x200000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x100000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x080000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x040000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x020000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x010000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x008000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x004000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x002000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x001000));
+ ShouldBeIdentical(SpecificNaN<float>(1, 17), SpecificNaN<float>(0, 0x7f0fff));
+ ShouldBeIdentical(SpecificNaN<float>(1, 17), SpecificNaN<float>(0, 0x7fff0f));
+
+ ShouldNotBeIdentical(UnspecifiedNaN<float>(), +0.0f);
+ ShouldNotBeIdentical(UnspecifiedNaN<float>(), -0.0f);
+ ShouldNotBeIdentical(UnspecifiedNaN<float>(), 1.0f);
+ ShouldNotBeIdentical(UnspecifiedNaN<float>(), -1.0f);
+ ShouldNotBeIdentical(UnspecifiedNaN<float>(), PositiveInfinity<float>());
+ ShouldNotBeIdentical(UnspecifiedNaN<float>(), NegativeInfinity<float>());
+}
+
+static void TestAreIdentical() {
+ TestDoublesAreIdentical();
+ TestFloatsAreIdentical();
+}
+
+static void TestDoubleExponentComponent() {
+ A(ExponentComponent(0.0) ==
+ -int_fast16_t(FloatingPoint<double>::kExponentBias));
+ A(ExponentComponent(-0.0) ==
+ -int_fast16_t(FloatingPoint<double>::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<double>()) ==
+ FloatingPoint<double>::kExponentBias + 1);
+ A(ExponentComponent(NegativeInfinity<double>()) ==
+ FloatingPoint<double>::kExponentBias + 1);
+ A(ExponentComponent(UnspecifiedNaN<double>()) ==
+ FloatingPoint<double>::kExponentBias + 1);
+}
+
+static void TestFloatExponentComponent() {
+ A(ExponentComponent(0.0f) ==
+ -int_fast16_t(FloatingPoint<float>::kExponentBias));
+ A(ExponentComponent(-0.0f) ==
+ -int_fast16_t(FloatingPoint<float>::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<float>()) ==
+ FloatingPoint<float>::kExponentBias + 1);
+ A(ExponentComponent(NegativeInfinity<float>()) ==
+ FloatingPoint<float>::kExponentBias + 1);
+ A(ExponentComponent(UnspecifiedNaN<float>()) ==
+ FloatingPoint<float>::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 <typename T>
+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 <typename T>
+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 <typename T>
+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 <typename T>
+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<double>()));
+ A(std::isnan(SpecificNaN<double>(1, 17)));
+ ;
+ A(std::isnan(SpecificNaN<double>(0, 0xfffffffffff0fULL)));
+ A(!std::isnan(PositiveInfinity<double>()));
+ A(!std::isnan(NegativeInfinity<double>()));
+
+ A(std::isinf(PositiveInfinity<double>()));
+ A(std::isinf(NegativeInfinity<double>()));
+ A(!std::isinf(UnspecifiedNaN<double>()));
+
+ A(!std::isfinite(PositiveInfinity<double>()));
+ A(!std::isfinite(NegativeInfinity<double>()));
+ A(!std::isfinite(UnspecifiedNaN<double>()));
+
+ A(!IsNegative(PositiveInfinity<double>()));
+ A(IsNegative(NegativeInfinity<double>()));
+ A(IsNegative(-0.0));
+ A(!IsNegative(0.0));
+ A(IsNegative(-1.0));
+ A(!IsNegative(1.0));
+
+ A(!IsNegativeZero(PositiveInfinity<double>()));
+ A(!IsNegativeZero(NegativeInfinity<double>()));
+ A(!IsNegativeZero(SpecificNaN<double>(1, 17)));
+ ;
+ A(!IsNegativeZero(SpecificNaN<double>(1, 0xfffffffffff0fULL)));
+ A(!IsNegativeZero(SpecificNaN<double>(0, 17)));
+ ;
+ A(!IsNegativeZero(SpecificNaN<double>(0, 0xfffffffffff0fULL)));
+ A(!IsNegativeZero(UnspecifiedNaN<double>()));
+ A(IsNegativeZero(-0.0));
+ A(!IsNegativeZero(0.0));
+ A(!IsNegativeZero(-1.0));
+ A(!IsNegativeZero(1.0));
+
+ // Edge case: negative zero.
+ TestEqualsIsForNegativeZero<double>();
+
+ // Int32 values.
+ auto testInt32 = TestEqualsIsForInt32<double>;
+ testInt32(0.0);
+ testInt32(1.0);
+ testInt32(INT32_MIN);
+ testInt32(INT32_MAX);
+
+ // Int64 values that don't fit in int32.
+ auto testInt64 = TestEqualsIsForInt64<double>;
+ 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<double>;
+ testNonInteger(NegativeInfinity<double>());
+ testNonInteger(PositiveInfinity<double>());
+ testNonInteger(UnspecifiedNaN<double>());
+ 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<float>()));
+ A(std::isnan(SpecificNaN<float>(1, 17)));
+ ;
+ A(std::isnan(SpecificNaN<float>(0, 0x7fff0fUL)));
+ A(!std::isnan(PositiveInfinity<float>()));
+ A(!std::isnan(NegativeInfinity<float>()));
+
+ A(std::isinf(PositiveInfinity<float>()));
+ A(std::isinf(NegativeInfinity<float>()));
+ A(!std::isinf(UnspecifiedNaN<float>()));
+
+ A(!std::isfinite(PositiveInfinity<float>()));
+ A(!std::isfinite(NegativeInfinity<float>()));
+ A(!std::isfinite(UnspecifiedNaN<float>()));
+
+ A(!IsNegative(PositiveInfinity<float>()));
+ A(IsNegative(NegativeInfinity<float>()));
+ A(IsNegative(-0.0f));
+ A(!IsNegative(0.0f));
+ A(IsNegative(-1.0f));
+ A(!IsNegative(1.0f));
+
+ A(!IsNegativeZero(PositiveInfinity<float>()));
+ A(!IsNegativeZero(NegativeInfinity<float>()));
+ A(!IsNegativeZero(SpecificNaN<float>(1, 17)));
+ ;
+ A(!IsNegativeZero(SpecificNaN<float>(1, 0x7fff0fUL)));
+ A(!IsNegativeZero(SpecificNaN<float>(0, 17)));
+ ;
+ A(!IsNegativeZero(SpecificNaN<float>(0, 0x7fff0fUL)));
+ A(!IsNegativeZero(UnspecifiedNaN<float>()));
+ A(IsNegativeZero(-0.0f));
+ A(!IsNegativeZero(0.0f));
+ A(!IsNegativeZero(-1.0f));
+ A(!IsNegativeZero(1.0f));
+
+ A(!IsPositiveZero(PositiveInfinity<float>()));
+ A(!IsPositiveZero(NegativeInfinity<float>()));
+ A(!IsPositiveZero(SpecificNaN<float>(1, 17)));
+ ;
+ A(!IsPositiveZero(SpecificNaN<float>(1, 0x7fff0fUL)));
+ A(!IsPositiveZero(SpecificNaN<float>(0, 17)));
+ ;
+ A(!IsPositiveZero(SpecificNaN<float>(0, 0x7fff0fUL)));
+ A(!IsPositiveZero(UnspecifiedNaN<float>()));
+ A(IsPositiveZero(0.0f));
+ A(!IsPositiveZero(-0.0f));
+ A(!IsPositiveZero(-1.0f));
+ A(!IsPositiveZero(1.0f));
+
+ // Edge case: negative zero.
+ TestEqualsIsForNegativeZero<float>();
+
+ // Int32 values.
+ auto testInt32 = TestEqualsIsForInt32<float>;
+ 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<float>;
+ 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<float>;
+ testNonInteger(NegativeInfinity<float>());
+ testNonInteger(PositiveInfinity<float>());
+ testNonInteger(UnspecifiedNaN<float>());
+ 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<float>::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<float>(1, 1), SpecificNaN<float>(1, 1)));
+ A(!FuzzyEqualsAdditive(SpecificNaN<float>(1, 2), SpecificNaN<float>(0, 8)));
+ A(!FuzzyEqualsMultiplicative(SpecificNaN<float>(1, 1),
+ SpecificNaN<float>(1, 1)));
+ A(!FuzzyEqualsMultiplicative(SpecificNaN<float>(1, 2),
+ SpecificNaN<float>(0, 200)));
+}
+
+static void TestDoublesAreApproximatelyEqual() {
+ double epsilon = mozilla::detail::FuzzyEqualsEpsilon<double>::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<double>(1, 1), SpecificNaN<double>(1, 1)));
+ A(!FuzzyEqualsAdditive(SpecificNaN<double>(1, 2), SpecificNaN<double>(0, 8)));
+ A(!FuzzyEqualsMultiplicative(SpecificNaN<double>(1, 1),
+ SpecificNaN<double>(1, 1)));
+ A(!FuzzyEqualsMultiplicative(SpecificNaN<double>(1, 2),
+ SpecificNaN<double>(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<double>()));
+ A(IsFloat32Representable(SpecificNaN<double>(0, 1)));
+ A(IsFloat32Representable(SpecificNaN<double>(0, 71389)));
+ A(IsFloat32Representable(SpecificNaN<double>(0, (uint64_t(1) << 52) - 2)));
+ A(IsFloat32Representable(SpecificNaN<double>(1, 1)));
+ A(IsFloat32Representable(SpecificNaN<double>(1, 71389)));
+ A(IsFloat32Representable(SpecificNaN<double>(1, (uint64_t(1) << 52) - 2)));
+ A(IsFloat32Representable(PositiveInfinity<double>()));
+ A(IsFloat32Representable(NegativeInfinity<double>()));
+
+ // 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<double>::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;
+}