diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt
index 59968e8c1c..cf87fd6e38 100644
--- a/Source/Core/Common/CMakeLists.txt
+++ b/Source/Core/Common/CMakeLists.txt
@@ -14,6 +14,7 @@ add_library(common
File.cpp
FileSearch.cpp
FileUtil.cpp
+ FloatUtils.cpp
GekkoDisassembler.cpp
Hash.cpp
HttpRequest.cpp
diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj
index 6c66dc871e..db4031ed0a 100644
--- a/Source/Core/Common/Common.vcxproj
+++ b/Source/Core/Common/Common.vcxproj
@@ -122,6 +122,7 @@
+
@@ -179,6 +180,7 @@
+
diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters
index c5deca0fd8..8e0817faf1 100644
--- a/Source/Core/Common/Common.vcxproj.filters
+++ b/Source/Core/Common/Common.vcxproj.filters
@@ -47,6 +47,7 @@
+
@@ -277,6 +278,7 @@
+
diff --git a/Source/Core/Common/FloatUtils.cpp b/Source/Core/Common/FloatUtils.cpp
new file mode 100644
index 0000000000..4bbb4266d6
--- /dev/null
+++ b/Source/Core/Common/FloatUtils.cpp
@@ -0,0 +1,216 @@
+// Copyright 2018 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "Common/FloatUtils.h"
+
+#include
+
+namespace Common
+{
+u32 ClassifyDouble(double dvalue)
+{
+ // TODO: Optimize the below to be as fast as possible.
+ IntDouble value(dvalue);
+ u64 sign = value.i & DOUBLE_SIGN;
+ u64 exp = value.i & DOUBLE_EXP;
+ if (exp > DOUBLE_ZERO && exp < DOUBLE_EXP)
+ {
+ // Nice normalized number.
+ return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN;
+ }
+ else
+ {
+ u64 mantissa = value.i & DOUBLE_FRAC;
+ if (mantissa)
+ {
+ if (exp)
+ {
+ return PPC_FPCLASS_QNAN;
+ }
+ else
+ {
+ // Denormalized number.
+ return sign ? PPC_FPCLASS_ND : PPC_FPCLASS_PD;
+ }
+ }
+ else if (exp)
+ {
+ // Infinite
+ return sign ? PPC_FPCLASS_NINF : PPC_FPCLASS_PINF;
+ }
+ else
+ {
+ // Zero
+ return sign ? PPC_FPCLASS_NZ : PPC_FPCLASS_PZ;
+ }
+ }
+}
+
+u32 ClassifyFloat(float fvalue)
+{
+ // TODO: Optimize the below to be as fast as possible.
+ IntFloat value(fvalue);
+ u32 sign = value.i & FLOAT_SIGN;
+ u32 exp = value.i & FLOAT_EXP;
+ if (exp > FLOAT_ZERO && exp < FLOAT_EXP)
+ {
+ // Nice normalized number.
+ return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN;
+ }
+ else
+ {
+ u32 mantissa = value.i & FLOAT_FRAC;
+ if (mantissa)
+ {
+ if (exp)
+ {
+ return PPC_FPCLASS_QNAN; // Quiet NAN
+ }
+ else
+ {
+ // Denormalized number.
+ return sign ? PPC_FPCLASS_ND : PPC_FPCLASS_PD;
+ }
+ }
+ else if (exp)
+ {
+ // Infinite
+ return sign ? PPC_FPCLASS_NINF : PPC_FPCLASS_PINF;
+ }
+ else
+ {
+ // Zero
+ return sign ? PPC_FPCLASS_NZ : PPC_FPCLASS_PZ;
+ }
+ }
+}
+
+const std::array frsqrte_expected = {{
+ {0x3ffa000, 0x7a4}, {0x3c29000, 0x700}, {0x38aa000, 0x670}, {0x3572000, 0x5f2},
+ {0x3279000, 0x584}, {0x2fb7000, 0x524}, {0x2d26000, 0x4cc}, {0x2ac0000, 0x47e},
+ {0x2881000, 0x43a}, {0x2665000, 0x3fa}, {0x2468000, 0x3c2}, {0x2287000, 0x38e},
+ {0x20c1000, 0x35e}, {0x1f12000, 0x332}, {0x1d79000, 0x30a}, {0x1bf4000, 0x2e6},
+ {0x1a7e800, 0x568}, {0x17cb800, 0x4f3}, {0x1552800, 0x48d}, {0x130c000, 0x435},
+ {0x10f2000, 0x3e7}, {0x0eff000, 0x3a2}, {0x0d2e000, 0x365}, {0x0b7c000, 0x32e},
+ {0x09e5000, 0x2fc}, {0x0867000, 0x2d0}, {0x06ff000, 0x2a8}, {0x05ab800, 0x283},
+ {0x046a000, 0x261}, {0x0339800, 0x243}, {0x0218800, 0x226}, {0x0105800, 0x20b},
+}};
+
+double ApproximateReciprocalSquareRoot(double val)
+{
+ union
+ {
+ double valf;
+ s64 vali;
+ };
+ valf = val;
+ s64 mantissa = vali & ((1LL << 52) - 1);
+ s64 sign = vali & (1ULL << 63);
+ s64 exponent = vali & (0x7FFLL << 52);
+
+ // Special case 0
+ if (mantissa == 0 && exponent == 0)
+ return sign ? -std::numeric_limits::infinity() :
+ std::numeric_limits::infinity();
+ // Special case NaN-ish numbers
+ if (exponent == (0x7FFLL << 52))
+ {
+ if (mantissa == 0)
+ {
+ if (sign)
+ return std::numeric_limits::quiet_NaN();
+
+ return 0.0;
+ }
+
+ return 0.0 + valf;
+ }
+
+ // Negative numbers return NaN
+ if (sign)
+ return std::numeric_limits::quiet_NaN();
+
+ if (!exponent)
+ {
+ // "Normalize" denormal values
+ do
+ {
+ exponent -= 1LL << 52;
+ mantissa <<= 1;
+ } while (!(mantissa & (1LL << 52)));
+ mantissa &= (1LL << 52) - 1;
+ exponent += 1LL << 52;
+ }
+
+ bool odd_exponent = !(exponent & (1LL << 52));
+ exponent = ((0x3FFLL << 52) - ((exponent - (0x3FELL << 52)) / 2)) & (0x7FFLL << 52);
+
+ int i = (int)(mantissa >> 37);
+ vali = sign | exponent;
+ int index = i / 2048 + (odd_exponent ? 16 : 0);
+ const auto& entry = frsqrte_expected[index];
+ vali |= (s64)(entry.m_base - entry.m_dec * (i % 2048)) << 26;
+ return valf;
+}
+
+const std::array fres_expected = {{
+ {0x7ff800, 0x3e1}, {0x783800, 0x3a7}, {0x70ea00, 0x371}, {0x6a0800, 0x340}, {0x638800, 0x313},
+ {0x5d6200, 0x2ea}, {0x579000, 0x2c4}, {0x520800, 0x2a0}, {0x4cc800, 0x27f}, {0x47ca00, 0x261},
+ {0x430800, 0x245}, {0x3e8000, 0x22a}, {0x3a2c00, 0x212}, {0x360800, 0x1fb}, {0x321400, 0x1e5},
+ {0x2e4a00, 0x1d1}, {0x2aa800, 0x1be}, {0x272c00, 0x1ac}, {0x23d600, 0x19b}, {0x209e00, 0x18b},
+ {0x1d8800, 0x17c}, {0x1a9000, 0x16e}, {0x17ae00, 0x15b}, {0x14f800, 0x15b}, {0x124400, 0x143},
+ {0x0fbe00, 0x143}, {0x0d3800, 0x12d}, {0x0ade00, 0x12d}, {0x088400, 0x11a}, {0x065000, 0x11a},
+ {0x041c00, 0x108}, {0x020c00, 0x106},
+}};
+
+// Used by fres and ps_res.
+double ApproximateReciprocal(double val)
+{
+ // We are using namespace std scoped here because the Android NDK is complete trash as usual
+ // For 32bit targets(mips, ARMv7, x86) it doesn't provide an implementation of std::copysign
+ // but instead provides just global namespace copysign implementations.
+ // The workaround for this is to just use namespace std within this function's scope
+ // That way on real toolchains it will use the std:: variant like normal.
+ using namespace std;
+ union
+ {
+ double valf;
+ s64 vali;
+ };
+
+ valf = val;
+ s64 mantissa = vali & ((1LL << 52) - 1);
+ s64 sign = vali & (1ULL << 63);
+ s64 exponent = vali & (0x7FFLL << 52);
+
+ // Special case 0
+ if (mantissa == 0 && exponent == 0)
+ return copysign(std::numeric_limits::infinity(), valf);
+
+ // Special case NaN-ish numbers
+ if (exponent == (0x7FFLL << 52))
+ {
+ if (mantissa == 0)
+ return copysign(0.0, valf);
+ return 0.0 + valf;
+ }
+
+ // Special case small inputs
+ if (exponent < (895LL << 52))
+ return copysign(std::numeric_limits::max(), valf);
+
+ // Special case large inputs
+ if (exponent >= (1149LL << 52))
+ return copysign(0.0, valf);
+
+ exponent = (0x7FDLL << 52) - exponent;
+
+ int i = (int)(mantissa >> 37);
+ const auto& entry = fres_expected[i / 1024];
+ vali = sign | exponent;
+ vali |= (s64)(entry.m_base - (entry.m_dec * (i % 1024) + 1) / 2) << 29;
+ return valf;
+}
+
+} // namespace Common
diff --git a/Source/Core/Common/FloatUtils.h b/Source/Core/Common/FloatUtils.h
new file mode 100644
index 0000000000..959a20bb17
--- /dev/null
+++ b/Source/Core/Common/FloatUtils.h
@@ -0,0 +1,139 @@
+// Copyright 2018 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+#include
+
+#include "Common/CommonTypes.h"
+
+namespace Common
+{
+template
+constexpr T SNANConstant()
+{
+ return std::numeric_limits::signaling_NaN();
+}
+
+#ifdef _MSC_VER
+
+// MSVC needs a workaround, because its std::numeric_limits::signaling_NaN()
+// will use __builtin_nans, which is improperly handled by the compiler and generates
+// a bad constant. Here we go back to the version MSVC used before the builtin.
+// TODO: Remove this and use numeric_limits directly whenever this bug is fixed.
+
+template <>
+constexpr double SNANConstant()
+{
+ return (_CSTD _Snan._Double);
+}
+template <>
+constexpr float SNANConstant()
+{
+ return (_CSTD _Snan._Float);
+}
+
+#endif
+
+// The most significant bit of the fraction is an is-quiet bit on all architectures we care about.
+enum : u64
+{
+ DOUBLE_SIGN = 0x8000000000000000ULL,
+ DOUBLE_EXP = 0x7FF0000000000000ULL,
+ DOUBLE_FRAC = 0x000FFFFFFFFFFFFFULL,
+ DOUBLE_ZERO = 0x0000000000000000ULL,
+ DOUBLE_QBIT = 0x0008000000000000ULL
+};
+
+enum : u32
+{
+ FLOAT_SIGN = 0x80000000,
+ FLOAT_EXP = 0x7F800000,
+ FLOAT_FRAC = 0x007FFFFF,
+ FLOAT_ZERO = 0x00000000
+};
+
+union IntDouble
+{
+ double d;
+ u64 i;
+
+ explicit IntDouble(u64 _i) : i(_i) {}
+ explicit IntDouble(double _d) : d(_d) {}
+};
+union IntFloat
+{
+ float f;
+ u32 i;
+
+ explicit IntFloat(u32 _i) : i(_i) {}
+ explicit IntFloat(float _f) : f(_f) {}
+};
+
+inline bool IsQNAN(double d)
+{
+ IntDouble x(d);
+ return ((x.i & DOUBLE_EXP) == DOUBLE_EXP) && ((x.i & DOUBLE_QBIT) == DOUBLE_QBIT);
+}
+
+inline bool IsSNAN(double d)
+{
+ IntDouble x(d);
+ return ((x.i & DOUBLE_EXP) == DOUBLE_EXP) && ((x.i & DOUBLE_FRAC) != DOUBLE_ZERO) &&
+ ((x.i & DOUBLE_QBIT) == DOUBLE_ZERO);
+}
+
+inline float FlushToZero(float f)
+{
+ IntFloat x(f);
+ if ((x.i & FLOAT_EXP) == 0)
+ {
+ x.i &= FLOAT_SIGN; // turn into signed zero
+ }
+ return x.f;
+}
+
+inline double FlushToZero(double d)
+{
+ IntDouble x(d);
+ if ((x.i & DOUBLE_EXP) == 0)
+ {
+ x.i &= DOUBLE_SIGN; // turn into signed zero
+ }
+ return x.d;
+}
+
+enum PPCFpClass
+{
+ PPC_FPCLASS_QNAN = 0x11,
+ PPC_FPCLASS_NINF = 0x9,
+ PPC_FPCLASS_NN = 0x8,
+ PPC_FPCLASS_ND = 0x18,
+ PPC_FPCLASS_NZ = 0x12,
+ PPC_FPCLASS_PZ = 0x2,
+ PPC_FPCLASS_PD = 0x14,
+ PPC_FPCLASS_PN = 0x4,
+ PPC_FPCLASS_PINF = 0x5,
+};
+
+// Uses PowerPC conventions for the return value, so it can be easily
+// used directly in CPU emulation.
+u32 ClassifyDouble(double dvalue);
+// More efficient float version.
+u32 ClassifyFloat(float fvalue);
+
+struct BaseAndDec
+{
+ int m_base;
+ int m_dec;
+};
+extern const std::array frsqrte_expected;
+extern const std::array fres_expected;
+
+// PowerPC approximation algorithms
+double ApproximateReciprocalSquareRoot(double val);
+double ApproximateReciprocal(double val);
+
+} // namespace Common
diff --git a/Source/Core/Common/MathUtil.cpp b/Source/Core/Common/MathUtil.cpp
index 0557c838b3..c1be813cc1 100644
--- a/Source/Core/Common/MathUtil.cpp
+++ b/Source/Core/Common/MathUtil.cpp
@@ -10,215 +10,6 @@
#include "Common/CommonTypes.h"
#include "Common/MathUtil.h"
-namespace MathUtil
-{
-u32 ClassifyDouble(double dvalue)
-{
- // TODO: Optimize the below to be as fast as possible.
- IntDouble value(dvalue);
- u64 sign = value.i & DOUBLE_SIGN;
- u64 exp = value.i & DOUBLE_EXP;
- if (exp > DOUBLE_ZERO && exp < DOUBLE_EXP)
- {
- // Nice normalized number.
- return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN;
- }
- else
- {
- u64 mantissa = value.i & DOUBLE_FRAC;
- if (mantissa)
- {
- if (exp)
- {
- return PPC_FPCLASS_QNAN;
- }
- else
- {
- // Denormalized number.
- return sign ? PPC_FPCLASS_ND : PPC_FPCLASS_PD;
- }
- }
- else if (exp)
- {
- // Infinite
- return sign ? PPC_FPCLASS_NINF : PPC_FPCLASS_PINF;
- }
- else
- {
- // Zero
- return sign ? PPC_FPCLASS_NZ : PPC_FPCLASS_PZ;
- }
- }
-}
-
-u32 ClassifyFloat(float fvalue)
-{
- // TODO: Optimize the below to be as fast as possible.
- IntFloat value(fvalue);
- u32 sign = value.i & FLOAT_SIGN;
- u32 exp = value.i & FLOAT_EXP;
- if (exp > FLOAT_ZERO && exp < FLOAT_EXP)
- {
- // Nice normalized number.
- return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN;
- }
- else
- {
- u32 mantissa = value.i & FLOAT_FRAC;
- if (mantissa)
- {
- if (exp)
- {
- return PPC_FPCLASS_QNAN; // Quiet NAN
- }
- else
- {
- // Denormalized number.
- return sign ? PPC_FPCLASS_ND : PPC_FPCLASS_PD;
- }
- }
- else if (exp)
- {
- // Infinite
- return sign ? PPC_FPCLASS_NINF : PPC_FPCLASS_PINF;
- }
- else
- {
- // Zero
- return sign ? PPC_FPCLASS_NZ : PPC_FPCLASS_PZ;
- }
- }
-}
-
-const std::array frsqrte_expected = {{
- {0x3ffa000, 0x7a4}, {0x3c29000, 0x700}, {0x38aa000, 0x670}, {0x3572000, 0x5f2},
- {0x3279000, 0x584}, {0x2fb7000, 0x524}, {0x2d26000, 0x4cc}, {0x2ac0000, 0x47e},
- {0x2881000, 0x43a}, {0x2665000, 0x3fa}, {0x2468000, 0x3c2}, {0x2287000, 0x38e},
- {0x20c1000, 0x35e}, {0x1f12000, 0x332}, {0x1d79000, 0x30a}, {0x1bf4000, 0x2e6},
- {0x1a7e800, 0x568}, {0x17cb800, 0x4f3}, {0x1552800, 0x48d}, {0x130c000, 0x435},
- {0x10f2000, 0x3e7}, {0x0eff000, 0x3a2}, {0x0d2e000, 0x365}, {0x0b7c000, 0x32e},
- {0x09e5000, 0x2fc}, {0x0867000, 0x2d0}, {0x06ff000, 0x2a8}, {0x05ab800, 0x283},
- {0x046a000, 0x261}, {0x0339800, 0x243}, {0x0218800, 0x226}, {0x0105800, 0x20b},
-}};
-
-double ApproximateReciprocalSquareRoot(double val)
-{
- union
- {
- double valf;
- s64 vali;
- };
- valf = val;
- s64 mantissa = vali & ((1LL << 52) - 1);
- s64 sign = vali & (1ULL << 63);
- s64 exponent = vali & (0x7FFLL << 52);
-
- // Special case 0
- if (mantissa == 0 && exponent == 0)
- return sign ? -std::numeric_limits::infinity() :
- std::numeric_limits::infinity();
- // Special case NaN-ish numbers
- if (exponent == (0x7FFLL << 52))
- {
- if (mantissa == 0)
- {
- if (sign)
- return std::numeric_limits::quiet_NaN();
-
- return 0.0;
- }
-
- return 0.0 + valf;
- }
-
- // Negative numbers return NaN
- if (sign)
- return std::numeric_limits::quiet_NaN();
-
- if (!exponent)
- {
- // "Normalize" denormal values
- do
- {
- exponent -= 1LL << 52;
- mantissa <<= 1;
- } while (!(mantissa & (1LL << 52)));
- mantissa &= (1LL << 52) - 1;
- exponent += 1LL << 52;
- }
-
- bool odd_exponent = !(exponent & (1LL << 52));
- exponent = ((0x3FFLL << 52) - ((exponent - (0x3FELL << 52)) / 2)) & (0x7FFLL << 52);
-
- int i = (int)(mantissa >> 37);
- vali = sign | exponent;
- int index = i / 2048 + (odd_exponent ? 16 : 0);
- const auto& entry = frsqrte_expected[index];
- vali |= (s64)(entry.m_base - entry.m_dec * (i % 2048)) << 26;
- return valf;
-}
-
-const std::array fres_expected = {{
- {0x7ff800, 0x3e1}, {0x783800, 0x3a7}, {0x70ea00, 0x371}, {0x6a0800, 0x340}, {0x638800, 0x313},
- {0x5d6200, 0x2ea}, {0x579000, 0x2c4}, {0x520800, 0x2a0}, {0x4cc800, 0x27f}, {0x47ca00, 0x261},
- {0x430800, 0x245}, {0x3e8000, 0x22a}, {0x3a2c00, 0x212}, {0x360800, 0x1fb}, {0x321400, 0x1e5},
- {0x2e4a00, 0x1d1}, {0x2aa800, 0x1be}, {0x272c00, 0x1ac}, {0x23d600, 0x19b}, {0x209e00, 0x18b},
- {0x1d8800, 0x17c}, {0x1a9000, 0x16e}, {0x17ae00, 0x15b}, {0x14f800, 0x15b}, {0x124400, 0x143},
- {0x0fbe00, 0x143}, {0x0d3800, 0x12d}, {0x0ade00, 0x12d}, {0x088400, 0x11a}, {0x065000, 0x11a},
- {0x041c00, 0x108}, {0x020c00, 0x106},
-}};
-
-// Used by fres and ps_res.
-double ApproximateReciprocal(double val)
-{
- // We are using namespace std scoped here because the Android NDK is complete trash as usual
- // For 32bit targets(mips, ARMv7, x86) it doesn't provide an implementation of std::copysign
- // but instead provides just global namespace copysign implementations.
- // The workaround for this is to just use namespace std within this function's scope
- // That way on real toolchains it will use the std:: variant like normal.
- using namespace std;
- union
- {
- double valf;
- s64 vali;
- };
-
- valf = val;
- s64 mantissa = vali & ((1LL << 52) - 1);
- s64 sign = vali & (1ULL << 63);
- s64 exponent = vali & (0x7FFLL << 52);
-
- // Special case 0
- if (mantissa == 0 && exponent == 0)
- return copysign(std::numeric_limits::infinity(), valf);
-
- // Special case NaN-ish numbers
- if (exponent == (0x7FFLL << 52))
- {
- if (mantissa == 0)
- return copysign(0.0, valf);
- return 0.0 + valf;
- }
-
- // Special case small inputs
- if (exponent < (895LL << 52))
- return copysign(std::numeric_limits::max(), valf);
-
- // Special case large inputs
- if (exponent >= (1149LL << 52))
- return copysign(0.0, valf);
-
- exponent = (0x7FDLL << 52) - exponent;
-
- int i = (int)(mantissa >> 37);
- const auto& entry = fres_expected[i / 1024];
- vali = sign | exponent;
- vali |= (s64)(entry.m_base - (entry.m_dec * (i % 1024) + 1) / 2) << 29;
- return valf;
-}
-
-} // namespace
-
inline void MatrixMul(int n, const float* a, const float* b, float* result)
{
for (int i = 0; i < n; ++i)
diff --git a/Source/Core/Common/MathUtil.h b/Source/Core/Common/MathUtil.h
index 58aad3522d..06ab1afd62 100644
--- a/Source/Core/Common/MathUtil.h
+++ b/Source/Core/Common/MathUtil.h
@@ -17,32 +17,6 @@
namespace MathUtil
{
-template
-constexpr T SNANConstant()
-{
- return std::numeric_limits::signaling_NaN();
-}
-
-#ifdef _MSC_VER
-
-// MSVC needs a workaround, because its std::numeric_limits::signaling_NaN()
-// will use __builtin_nans, which is improperly handled by the compiler and generates
-// a bad constant. Here we go back to the version MSVC used before the builtin.
-// TODO: Remove this and use numeric_limits directly whenever this bug is fixed.
-
-template <>
-constexpr double SNANConstant()
-{
- return (_CSTD _Snan._Double);
-}
-template <>
-constexpr float SNANConstant()
-{
- return (_CSTD _Snan._Float);
-}
-
-#endif
-
template
constexpr T Clamp(const T val, const T& min, const T& max)
{
@@ -55,96 +29,6 @@ constexpr bool IsPow2(T imm)
return imm > 0 && (imm & (imm - 1)) == 0;
}
-// The most significant bit of the fraction is an is-quiet bit on all architectures we care about.
-
-static const u64 DOUBLE_SIGN = 0x8000000000000000ULL, DOUBLE_EXP = 0x7FF0000000000000ULL,
- DOUBLE_FRAC = 0x000FFFFFFFFFFFFFULL, DOUBLE_ZERO = 0x0000000000000000ULL,
- DOUBLE_QBIT = 0x0008000000000000ULL;
-
-static const u32 FLOAT_SIGN = 0x80000000, FLOAT_EXP = 0x7F800000, FLOAT_FRAC = 0x007FFFFF,
- FLOAT_ZERO = 0x00000000;
-
-union IntDouble
-{
- double d;
- u64 i;
-
- explicit IntDouble(u64 _i) : i(_i) {}
- explicit IntDouble(double _d) : d(_d) {}
-};
-union IntFloat
-{
- float f;
- u32 i;
-
- explicit IntFloat(u32 _i) : i(_i) {}
- explicit IntFloat(float _f) : f(_f) {}
-};
-
-inline bool IsQNAN(double d)
-{
- IntDouble x(d);
- return ((x.i & DOUBLE_EXP) == DOUBLE_EXP) && ((x.i & DOUBLE_QBIT) == DOUBLE_QBIT);
-}
-
-inline bool IsSNAN(double d)
-{
- IntDouble x(d);
- return ((x.i & DOUBLE_EXP) == DOUBLE_EXP) && ((x.i & DOUBLE_FRAC) != DOUBLE_ZERO) &&
- ((x.i & DOUBLE_QBIT) == DOUBLE_ZERO);
-}
-
-inline float FlushToZero(float f)
-{
- IntFloat x(f);
- if ((x.i & FLOAT_EXP) == 0)
- {
- x.i &= FLOAT_SIGN; // turn into signed zero
- }
- return x.f;
-}
-
-inline double FlushToZero(double d)
-{
- IntDouble x(d);
- if ((x.i & DOUBLE_EXP) == 0)
- {
- x.i &= DOUBLE_SIGN; // turn into signed zero
- }
- return x.d;
-}
-
-enum PPCFpClass
-{
- PPC_FPCLASS_QNAN = 0x11,
- PPC_FPCLASS_NINF = 0x9,
- PPC_FPCLASS_NN = 0x8,
- PPC_FPCLASS_ND = 0x18,
- PPC_FPCLASS_NZ = 0x12,
- PPC_FPCLASS_PZ = 0x2,
- PPC_FPCLASS_PD = 0x14,
- PPC_FPCLASS_PN = 0x4,
- PPC_FPCLASS_PINF = 0x5,
-};
-
-// Uses PowerPC conventions for the return value, so it can be easily
-// used directly in CPU emulation.
-u32 ClassifyDouble(double dvalue);
-// More efficient float version.
-u32 ClassifyFloat(float fvalue);
-
-struct BaseAndDec
-{
- int m_base;
- int m_dec;
-};
-extern const std::array frsqrte_expected;
-extern const std::array fres_expected;
-
-// PowerPC approximation algorithms
-double ApproximateReciprocalSquareRoot(double val);
-double ApproximateReciprocal(double val);
-
template
struct Rectangle
{
diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h
index ce2c1485a1..593dc10bbb 100644
--- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h
+++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h
@@ -10,7 +10,7 @@
#include "Common/CPUDetect.h"
#include "Common/CommonTypes.h"
-#include "Common/MathUtil.h"
+#include "Common/FloatUtils.h"
#include "Core/PowerPC/Gekko.h"
#include "Core/PowerPC/PowerPC.h"
@@ -56,7 +56,7 @@ inline double ForceSingle(double value)
float x = (float)value;
if (!cpu_info.bFlushToZero && FPSCR.NI)
{
- x = MathUtil::FlushToZero(x);
+ x = Common::FlushToZero(x);
}
// ...and back to double:
return x;
@@ -66,7 +66,7 @@ inline double ForceDouble(double d)
{
if (!cpu_info.bFlushToZero && FPSCR.NI)
{
- d = MathUtil::FlushToZero(d);
+ d = Common::FlushToZero(d);
}
return d;
}
@@ -89,7 +89,7 @@ inline double MakeQuiet(double d)
u64 integral;
std::memcpy(&integral, &d, sizeof(u64));
- integral |= MathUtil::DOUBLE_QBIT;
+ integral |= Common::DOUBLE_QBIT;
double result;
std::memcpy(&result, &integral, sizeof(double));
@@ -227,13 +227,13 @@ inline double NI_msub(double a, double c, double b)
inline u32 ConvertToSingle(u64 x)
{
u32 exp = (x >> 52) & 0x7ff;
- if (exp > 896 || (x & ~MathUtil::DOUBLE_SIGN) == 0)
+ if (exp > 896 || (x & ~Common::DOUBLE_SIGN) == 0)
{
return ((x >> 32) & 0xc0000000) | ((x >> 29) & 0x3fffffff);
}
else if (exp >= 874)
{
- u32 t = (u32)(0x80000000 | ((x & MathUtil::DOUBLE_FRAC) >> 21));
+ u32 t = (u32)(0x80000000 | ((x & Common::DOUBLE_FRAC) >> 21));
t = t >> (905 - exp);
t |= (x >> 32) & 0x80000000;
return t;
@@ -250,7 +250,7 @@ inline u32 ConvertToSingle(u64 x)
inline u32 ConvertToSingleFTZ(u64 x)
{
u32 exp = (x >> 52) & 0x7ff;
- if (exp > 896 || (x & ~MathUtil::DOUBLE_SIGN) == 0)
+ if (exp > 896 || (x & ~Common::DOUBLE_SIGN) == 0)
{
return ((x >> 32) & 0xc0000000) | ((x >> 29) & 0x3fffffff);
}
diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp
index 1adb50074d..5ef3ff035a 100644
--- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp
+++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp
@@ -6,7 +6,7 @@
#include
#include "Common/CommonTypes.h"
-#include "Common/MathUtil.h"
+#include "Common/FloatUtils.h"
#include "Core/PowerPC/Interpreter/Interpreter.h"
#include "Core/PowerPC/Interpreter/Interpreter_FPUtils.h"
#include "Core/PowerPC/PowerPC.h"
@@ -25,7 +25,7 @@ void Interpreter::Helper_FloatCompareOrdered(UGeckoInstruction inst, double fa,
if (std::isnan(fa) || std::isnan(fb))
{
compare_result = FPCC::FU;
- if (MathUtil::IsSNAN(fa) || MathUtil::IsSNAN(fb))
+ if (Common::IsSNAN(fa) || Common::IsSNAN(fb))
{
SetFPException(FPSCR_VXSNAN);
if (FPSCR.VE == 0)
@@ -67,7 +67,7 @@ void Interpreter::Helper_FloatCompareUnordered(UGeckoInstruction inst, double fa
{
compare_result = FPCC::FU;
- if (MathUtil::IsSNAN(fa) || MathUtil::IsSNAN(fb))
+ if (Common::IsSNAN(fa) || Common::IsSNAN(fb))
{
SetFPException(FPSCR_VXSNAN);
}
@@ -373,7 +373,7 @@ void Interpreter::fdivsx(UGeckoInstruction inst)
void Interpreter::fresx(UGeckoInstruction inst)
{
double b = rPS0(inst.FB);
- rPS0(inst.FD) = rPS1(inst.FD) = MathUtil::ApproximateReciprocal(b);
+ rPS0(inst.FD) = rPS1(inst.FD) = Common::ApproximateReciprocal(b);
if (b == 0.0)
{
@@ -399,7 +399,7 @@ void Interpreter::frsqrtex(UGeckoInstruction inst)
SetFPException(FPSCR_ZX);
}
- rPS0(inst.FD) = MathUtil::ApproximateReciprocalSquareRoot(b);
+ rPS0(inst.FD) = Common::ApproximateReciprocalSquareRoot(b);
PowerPC::UpdateFPRF(rPS0(inst.FD));
if (inst.Rc)
diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp
index 38df41895a..6a84132c96 100644
--- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp
+++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp
@@ -5,7 +5,7 @@
#include
#include "Common/CommonTypes.h"
-#include "Common/MathUtil.h"
+#include "Common/FloatUtils.h"
#include "Core/PowerPC/Interpreter/Interpreter.h"
#include "Core/PowerPC/Interpreter/Interpreter_FPUtils.h"
#include "Core/PowerPC/PowerPC.h"
@@ -123,8 +123,8 @@ void Interpreter::ps_res(UGeckoInstruction inst)
SetFPException(FPSCR_ZX);
}
- rPS0(inst.FD) = MathUtil::ApproximateReciprocal(a);
- rPS1(inst.FD) = MathUtil::ApproximateReciprocal(b);
+ rPS0(inst.FD) = Common::ApproximateReciprocal(a);
+ rPS1(inst.FD) = Common::ApproximateReciprocal(b);
PowerPC::UpdateFPRF(rPS0(inst.FD));
if (inst.Rc)
@@ -143,8 +143,8 @@ void Interpreter::ps_rsqrte(UGeckoInstruction inst)
SetFPException(FPSCR_VXSQRT);
}
- rPS0(inst.FD) = ForceSingle(MathUtil::ApproximateReciprocalSquareRoot(rPS0(inst.FB)));
- rPS1(inst.FD) = ForceSingle(MathUtil::ApproximateReciprocalSquareRoot(rPS1(inst.FB)));
+ rPS0(inst.FD) = ForceSingle(Common::ApproximateReciprocalSquareRoot(rPS0(inst.FB)));
+ rPS1(inst.FD) = ForceSingle(Common::ApproximateReciprocalSquareRoot(rPS1(inst.FB)));
PowerPC::UpdateFPRF(rPS0(inst.FD));
diff --git a/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.cpp b/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.cpp
index ab4d496a98..6f92d605a7 100644
--- a/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.cpp
+++ b/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.cpp
@@ -9,8 +9,8 @@
#include "Common/Assert.h"
#include "Common/CPUDetect.h"
+#include "Common/FloatUtils.h"
#include "Common/Intrinsics.h"
-#include "Common/MathUtil.h"
#include "Core/HW/MMIO.h"
#include "Core/HW/Memmap.h"
#include "Core/PowerPC/Gekko.h"
@@ -1060,8 +1060,7 @@ void EmuCodeBlock::SetFPRF(Gen::X64Reg xmm)
// Nice normalized number: sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN;
LEA(32, RSCRATCH,
- MScaled(RSCRATCH, MathUtil::PPC_FPCLASS_NN - MathUtil::PPC_FPCLASS_PN,
- MathUtil::PPC_FPCLASS_PN));
+ MScaled(RSCRATCH, Common::PPC_FPCLASS_NN - Common::PPC_FPCLASS_PN, Common::PPC_FPCLASS_PN));
continue1 = J();
SetJumpTarget(maxExponent);
@@ -1069,14 +1068,14 @@ void EmuCodeBlock::SetFPRF(Gen::X64Reg xmm)
FixupBranch notNAN = J_CC(CC_Z);
// Max exponent + mantissa: PPC_FPCLASS_QNAN
- MOV(32, R(RSCRATCH), Imm32(MathUtil::PPC_FPCLASS_QNAN));
+ MOV(32, R(RSCRATCH), Imm32(Common::PPC_FPCLASS_QNAN));
continue2 = J();
// Max exponent + no mantissa: sign ? PPC_FPCLASS_NINF : PPC_FPCLASS_PINF;
SetJumpTarget(notNAN);
LEA(32, RSCRATCH,
- MScaled(RSCRATCH, MathUtil::PPC_FPCLASS_NINF - MathUtil::PPC_FPCLASS_PINF,
- MathUtil::PPC_FPCLASS_PINF));
+ MScaled(RSCRATCH, Common::PPC_FPCLASS_NINF - Common::PPC_FPCLASS_PINF,
+ Common::PPC_FPCLASS_PINF));
continue3 = J();
SetJumpTarget(zeroExponent);
@@ -1085,14 +1084,13 @@ void EmuCodeBlock::SetFPRF(Gen::X64Reg xmm)
// No exponent + mantissa: sign ? PPC_FPCLASS_ND : PPC_FPCLASS_PD;
LEA(32, RSCRATCH,
- MScaled(RSCRATCH, MathUtil::PPC_FPCLASS_ND - MathUtil::PPC_FPCLASS_PD,
- MathUtil::PPC_FPCLASS_PD));
+ MScaled(RSCRATCH, Common::PPC_FPCLASS_ND - Common::PPC_FPCLASS_PD, Common::PPC_FPCLASS_PD));
continue4 = J();
// Zero: sign ? PPC_FPCLASS_NZ : PPC_FPCLASS_PZ;
SetJumpTarget(zero);
SHL(32, R(RSCRATCH), Imm8(4));
- ADD(32, R(RSCRATCH), Imm8(MathUtil::PPC_FPCLASS_PZ));
+ ADD(32, R(RSCRATCH), Imm8(Common::PPC_FPCLASS_PZ));
}
else
{
@@ -1107,33 +1105,31 @@ void EmuCodeBlock::SetFPRF(Gen::X64Reg xmm)
MOVQ_xmm(R(RSCRATCH), xmm);
SHR(64, R(RSCRATCH), Imm8(63));
LEA(32, RSCRATCH,
- MScaled(RSCRATCH, MathUtil::PPC_FPCLASS_NN - MathUtil::PPC_FPCLASS_PN,
- MathUtil::PPC_FPCLASS_PN));
+ MScaled(RSCRATCH, Common::PPC_FPCLASS_NN - Common::PPC_FPCLASS_PN, Common::PPC_FPCLASS_PN));
continue1 = J();
SetJumpTarget(nan);
MOVQ_xmm(R(RSCRATCH), xmm);
SHR(64, R(RSCRATCH), Imm8(63));
- MOV(32, R(RSCRATCH), Imm32(MathUtil::PPC_FPCLASS_QNAN));
+ MOV(32, R(RSCRATCH), Imm32(Common::PPC_FPCLASS_QNAN));
continue2 = J();
SetJumpTarget(infinity);
MOVQ_xmm(R(RSCRATCH), xmm);
SHR(64, R(RSCRATCH), Imm8(63));
LEA(32, RSCRATCH,
- MScaled(RSCRATCH, MathUtil::PPC_FPCLASS_NINF - MathUtil::PPC_FPCLASS_PINF,
- MathUtil::PPC_FPCLASS_PINF));
+ MScaled(RSCRATCH, Common::PPC_FPCLASS_NINF - Common::PPC_FPCLASS_PINF,
+ Common::PPC_FPCLASS_PINF));
continue3 = J();
SetJumpTarget(zeroExponent);
TEST(64, R(RSCRATCH), R(RSCRATCH));
FixupBranch zero = J_CC(CC_Z);
SHR(64, R(RSCRATCH), Imm8(63));
LEA(32, RSCRATCH,
- MScaled(RSCRATCH, MathUtil::PPC_FPCLASS_ND - MathUtil::PPC_FPCLASS_PD,
- MathUtil::PPC_FPCLASS_PD));
+ MScaled(RSCRATCH, Common::PPC_FPCLASS_ND - Common::PPC_FPCLASS_PD, Common::PPC_FPCLASS_PD));
continue4 = J();
SetJumpTarget(zero);
SHR(64, R(RSCRATCH), Imm8(63));
SHL(32, R(RSCRATCH), Imm8(4));
- ADD(32, R(RSCRATCH), Imm8(MathUtil::PPC_FPCLASS_PZ));
+ ADD(32, R(RSCRATCH), Imm8(Common::PPC_FPCLASS_PZ));
}
SetJumpTarget(continue1);
diff --git a/Source/Core/Core/PowerPC/Jit64Common/Jit64AsmCommon.cpp b/Source/Core/Core/PowerPC/Jit64Common/Jit64AsmCommon.cpp
index 86f29611e7..68e1251dcb 100644
--- a/Source/Core/Core/PowerPC/Jit64Common/Jit64AsmCommon.cpp
+++ b/Source/Core/Core/PowerPC/Jit64Common/Jit64AsmCommon.cpp
@@ -8,8 +8,8 @@
#include "Common/CPUDetect.h"
#include "Common/CommonTypes.h"
+#include "Common/FloatUtils.h"
#include "Common/JitRegister.h"
-#include "Common/MathUtil.h"
#include "Common/x64ABI.h"
#include "Common/x64Emitter.h"
#include "Core/PowerPC/Gekko.h"
@@ -57,15 +57,15 @@ void CommonAsmRoutines::GenFrsqrte()
XOR(32, R(RSCRATCH_EXTRA), Imm8(0x10)); // int index = i / 2048 + (odd_exponent ? 16 : 0);
PUSH(RSCRATCH2);
- MOV(64, R(RSCRATCH2), ImmPtr(GetConstantFromPool(MathUtil::frsqrte_expected)));
- static_assert(sizeof(MathUtil::BaseAndDec) == 8, "Unable to use SCALE_8; incorrect size");
+ MOV(64, R(RSCRATCH2), ImmPtr(GetConstantFromPool(Common::frsqrte_expected)));
+ static_assert(sizeof(Common::BaseAndDec) == 8, "Unable to use SCALE_8; incorrect size");
SHR(64, R(RSCRATCH), Imm8(37));
AND(32, R(RSCRATCH), Imm32(0x7FF));
IMUL(32, RSCRATCH,
- MComplex(RSCRATCH2, RSCRATCH_EXTRA, SCALE_8, offsetof(MathUtil::BaseAndDec, m_dec)));
+ MComplex(RSCRATCH2, RSCRATCH_EXTRA, SCALE_8, offsetof(Common::BaseAndDec, m_dec)));
MOV(32, R(RSCRATCH_EXTRA),
- MComplex(RSCRATCH2, RSCRATCH_EXTRA, SCALE_8, offsetof(MathUtil::BaseAndDec, m_base)));
+ MComplex(RSCRATCH2, RSCRATCH_EXTRA, SCALE_8, offsetof(Common::BaseAndDec, m_base)));
SUB(32, R(RSCRATCH_EXTRA), R(RSCRATCH));
SHL(64, R(RSCRATCH_EXTRA), Imm8(26));
@@ -94,7 +94,7 @@ void CommonAsmRoutines::GenFrsqrte()
SetJumpTarget(complex2);
SetJumpTarget(complex3);
ABI_PushRegistersAndAdjustStack(QUANTIZED_REGS_TO_SAVE, 8);
- ABI_CallFunction(MathUtil::ApproximateReciprocalSquareRoot);
+ ABI_CallFunction(Common::ApproximateReciprocalSquareRoot);
ABI_PopRegistersAndAdjustStack(QUANTIZED_REGS_TO_SAVE, 8);
RET();
@@ -135,16 +135,16 @@ void CommonAsmRoutines::GenFres()
AND(32, R(RSCRATCH2), Imm8(0x1F)); // i / 1024
PUSH(RSCRATCH_EXTRA);
- MOV(64, R(RSCRATCH_EXTRA), ImmPtr(GetConstantFromPool(MathUtil::fres_expected)));
- static_assert(sizeof(MathUtil::BaseAndDec) == 8, "Unable to use SCALE_8; incorrect size");
+ MOV(64, R(RSCRATCH_EXTRA), ImmPtr(GetConstantFromPool(Common::fres_expected)));
+ static_assert(sizeof(Common::BaseAndDec) == 8, "Unable to use SCALE_8; incorrect size");
IMUL(32, RSCRATCH,
- MComplex(RSCRATCH_EXTRA, RSCRATCH2, SCALE_8, offsetof(MathUtil::BaseAndDec, m_dec)));
+ MComplex(RSCRATCH_EXTRA, RSCRATCH2, SCALE_8, offsetof(Common::BaseAndDec, m_dec)));
ADD(32, R(RSCRATCH), Imm8(1));
SHR(32, R(RSCRATCH), Imm8(1));
MOV(32, R(RSCRATCH2),
- MComplex(RSCRATCH_EXTRA, RSCRATCH2, SCALE_8, offsetof(MathUtil::BaseAndDec, m_base)));
+ MComplex(RSCRATCH_EXTRA, RSCRATCH2, SCALE_8, offsetof(Common::BaseAndDec, m_base)));
SUB(32, R(RSCRATCH2), R(RSCRATCH));
SHL(64, R(RSCRATCH2), Imm8(29));
@@ -165,7 +165,7 @@ void CommonAsmRoutines::GenFres()
SetJumpTarget(complex);
ABI_PushRegistersAndAdjustStack(QUANTIZED_REGS_TO_SAVE, 8);
- ABI_CallFunction(MathUtil::ApproximateReciprocal);
+ ABI_CallFunction(Common::ApproximateReciprocal);
ABI_PopRegistersAndAdjustStack(QUANTIZED_REGS_TO_SAVE, 8);
RET();
diff --git a/Source/Core/Core/PowerPC/PowerPC.cpp b/Source/Core/Core/PowerPC/PowerPC.cpp
index dd35ae801c..bd92ae893b 100644
--- a/Source/Core/Core/PowerPC/PowerPC.cpp
+++ b/Source/Core/Core/PowerPC/PowerPC.cpp
@@ -11,8 +11,8 @@
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
#include "Common/FPURoundMode.h"
+#include "Common/FloatUtils.h"
#include "Common/Logging/Log.h"
-#include "Common/MathUtil.h"
#include "Core/ConfigManager.h"
#include "Core/CoreTiming.h"
@@ -563,7 +563,7 @@ void CheckBreakPoints()
void UpdateFPRF(double dvalue)
{
- FPSCR.FPRF = MathUtil::ClassifyDouble(dvalue);
+ FPSCR.FPRF = Common::ClassifyDouble(dvalue);
}
} // namespace PowerPC
diff --git a/Source/UnitTests/Common/CMakeLists.txt b/Source/UnitTests/Common/CMakeLists.txt
index 7154e86131..8b94438c30 100644
--- a/Source/UnitTests/Common/CMakeLists.txt
+++ b/Source/UnitTests/Common/CMakeLists.txt
@@ -7,6 +7,7 @@ add_dolphin_test(CommonFuncsTest CommonFuncsTest.cpp)
add_dolphin_test(EventTest EventTest.cpp)
add_dolphin_test(FixedSizeQueueTest FixedSizeQueueTest.cpp)
add_dolphin_test(FlagTest FlagTest.cpp)
+add_dolphin_test(FloatUtilsTest FloatUtilsTest.cpp)
add_dolphin_test(MathUtilTest MathUtilTest.cpp)
add_dolphin_test(NandPathsTest NandPathsTest.cpp)
add_dolphin_test(SPSCQueueTest SPSCQueueTest.cpp)
diff --git a/Source/UnitTests/Common/FloatUtilsTest.cpp b/Source/UnitTests/Common/FloatUtilsTest.cpp
new file mode 100644
index 0000000000..c1f7117162
--- /dev/null
+++ b/Source/UnitTests/Common/FloatUtilsTest.cpp
@@ -0,0 +1,68 @@
+// Copyright 2018 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include
+#include
+
+#include
+
+#include "Common/FloatUtils.h"
+
+TEST(FloatUtils, IsQNAN)
+{
+ EXPECT_TRUE(Common::IsQNAN(std::numeric_limits::quiet_NaN()));
+ EXPECT_FALSE(Common::IsQNAN(Common::SNANConstant()));
+}
+
+TEST(FloatUtils, IsSNAN)
+{
+ EXPECT_FALSE(Common::IsSNAN(std::numeric_limits::quiet_NaN()));
+ EXPECT_TRUE(Common::IsSNAN(Common::SNANConstant()));
+}
+
+TEST(FloatUtils, FlushToZero)
+{
+ // To test the software implementation we need to make sure FTZ and DAZ are disabled.
+ // Using volatile here to ensure the compiler doesn't constant-fold it,
+ // we want the multiplication to occur at test runtime.
+ volatile float s = std::numeric_limits::denorm_min();
+ volatile double d = std::numeric_limits::denorm_min();
+ // Casting away the volatile attribute is required in order for msvc to resolve this to the
+ // correct instance of the comparison function.
+ EXPECT_LT(0.f, (float)(s * 2));
+ EXPECT_LT(0.0, (double)(d * 2));
+
+ EXPECT_EQ(+0.0, Common::FlushToZero(+std::numeric_limits::denorm_min()));
+ EXPECT_EQ(-0.0, Common::FlushToZero(-std::numeric_limits::denorm_min()));
+ EXPECT_EQ(+0.0, Common::FlushToZero(+std::numeric_limits::min() / 2));
+ EXPECT_EQ(-0.0, Common::FlushToZero(-std::numeric_limits::min() / 2));
+ EXPECT_EQ(std::numeric_limits::min(),
+ Common::FlushToZero(std::numeric_limits::min()));
+ EXPECT_EQ(std::numeric_limits::max(),
+ Common::FlushToZero(std::numeric_limits::max()));
+ EXPECT_EQ(+std::numeric_limits::infinity(),
+ Common::FlushToZero(+std::numeric_limits::infinity()));
+ EXPECT_EQ(-std::numeric_limits::infinity(),
+ Common::FlushToZero(-std::numeric_limits::infinity()));
+
+ // Test all subnormals as well as an equally large set of random normal floats.
+ std::default_random_engine engine(0);
+ std::uniform_int_distribution dist(0x00800000u, 0x7fffffffu);
+ for (u32 i = 0; i <= 0x007fffffu; ++i)
+ {
+ Common::IntFloat x(i);
+ EXPECT_EQ(+0.f, Common::FlushToZero(x.f));
+
+ x.i = i | 0x80000000u;
+ EXPECT_EQ(-0.f, Common::FlushToZero(x.f));
+
+ x.i = dist(engine);
+ Common::IntFloat y(Common::FlushToZero(x.f));
+ EXPECT_EQ(x.i, y.i);
+
+ x.i |= 0x80000000u;
+ y.f = Common::FlushToZero(x.f);
+ EXPECT_EQ(x.i, y.i);
+ }
+}
diff --git a/Source/UnitTests/Common/MathUtilTest.cpp b/Source/UnitTests/Common/MathUtilTest.cpp
index 9c84ad6f0a..4b21ace1c4 100644
--- a/Source/UnitTests/Common/MathUtilTest.cpp
+++ b/Source/UnitTests/Common/MathUtilTest.cpp
@@ -3,8 +3,6 @@
// Refer to the license.txt file included.
#include
-#include
-#include
#include "Common/MathUtil.h"
@@ -20,18 +18,6 @@ TEST(MathUtil, Clamp)
EXPECT_EQ(0.0, MathUtil::Clamp(-1.0, 0.0, 2.0));
}
-TEST(MathUtil, IsQNAN)
-{
- EXPECT_TRUE(MathUtil::IsQNAN(std::numeric_limits::quiet_NaN()));
- EXPECT_FALSE(MathUtil::IsQNAN(MathUtil::SNANConstant()));
-}
-
-TEST(MathUtil, IsSNAN)
-{
- EXPECT_FALSE(MathUtil::IsSNAN(std::numeric_limits::quiet_NaN()));
- EXPECT_TRUE(MathUtil::IsSNAN(MathUtil::SNANConstant()));
-}
-
TEST(MathUtil, IntLog2)
{
EXPECT_EQ(0, IntLog2(1));
@@ -44,49 +30,3 @@ TEST(MathUtil, IntLog2)
EXPECT_EQ(3, IntLog2(15));
EXPECT_EQ(63, IntLog2(0xFFFFFFFFFFFFFFFFull));
}
-
-TEST(MathUtil, FlushToZero)
-{
- // To test the software implementation we need to make sure FTZ and DAZ are disabled.
- // Using volatile here to ensure the compiler doesn't constant-fold it,
- // we want the multiplication to occur at test runtime.
- volatile float s = std::numeric_limits::denorm_min();
- volatile double d = std::numeric_limits::denorm_min();
- // Casting away the volatile attribute is required in order for msvc to resolve this to the
- // correct instance of the comparison function.
- EXPECT_LT(0.f, (float)(s * 2));
- EXPECT_LT(0.0, (double)(d * 2));
-
- EXPECT_EQ(+0.0, MathUtil::FlushToZero(+std::numeric_limits::denorm_min()));
- EXPECT_EQ(-0.0, MathUtil::FlushToZero(-std::numeric_limits::denorm_min()));
- EXPECT_EQ(+0.0, MathUtil::FlushToZero(+std::numeric_limits::min() / 2));
- EXPECT_EQ(-0.0, MathUtil::FlushToZero(-std::numeric_limits::min() / 2));
- EXPECT_EQ(std::numeric_limits::min(),
- MathUtil::FlushToZero(std::numeric_limits::min()));
- EXPECT_EQ(std::numeric_limits::max(),
- MathUtil::FlushToZero(std::numeric_limits::max()));
- EXPECT_EQ(+std::numeric_limits::infinity(),
- MathUtil::FlushToZero(+std::numeric_limits::infinity()));
- EXPECT_EQ(-std::numeric_limits::infinity(),
- MathUtil::FlushToZero(-std::numeric_limits::infinity()));
-
- // Test all subnormals as well as an equally large set of random normal floats.
- std::default_random_engine engine(0);
- std::uniform_int_distribution dist(0x00800000u, 0x7fffffffu);
- for (u32 i = 0; i <= 0x007fffffu; ++i)
- {
- MathUtil::IntFloat x(i);
- EXPECT_EQ(+0.f, MathUtil::FlushToZero(x.f));
-
- x.i = i | 0x80000000u;
- EXPECT_EQ(-0.f, MathUtil::FlushToZero(x.f));
-
- x.i = dist(engine);
- MathUtil::IntFloat y(MathUtil::FlushToZero(x.f));
- EXPECT_EQ(x.i, y.i);
-
- x.i |= 0x80000000u;
- y.f = MathUtil::FlushToZero(x.f);
- EXPECT_EQ(x.i, y.i);
- }
-}
diff --git a/Source/UnitTests/VideoCommon/VertexLoaderTest.cpp b/Source/UnitTests/VideoCommon/VertexLoaderTest.cpp
index 54df13351a..6ba3c4c464 100644
--- a/Source/UnitTests/VideoCommon/VertexLoaderTest.cpp
+++ b/Source/UnitTests/VideoCommon/VertexLoaderTest.cpp
@@ -11,7 +11,7 @@
#include // NOLINT
#include "Common/Common.h"
-#include "Common/MathUtil.h"
+#include "Common/FloatUtils.h"
#include "VideoCommon/CPMemory.h"
#include "VideoCommon/DataReader.h"
#include "VideoCommon/OpcodeDecoding.h"
@@ -75,7 +75,7 @@ protected:
void ExpectOut(float val)
{
// Read unswapped.
- MathUtil::IntFloat expected(val), actual(m_dst.Read());
+ Common::IntFloat expected(val), actual(m_dst.Read());
if (!actual.f || actual.f != actual.f)
EXPECT_EQ(expected.i, actual.i);
else