mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-02-02 11:02:28 +02:00
Merge pull request #6777 from lioncash/header
Common: Move floating-point utility functions to FloatUtils.h/.cpp
This commit is contained in:
commit
f97711ac02
@ -14,6 +14,7 @@ add_library(common
|
||||
File.cpp
|
||||
FileSearch.cpp
|
||||
FileUtil.cpp
|
||||
FloatUtils.cpp
|
||||
GekkoDisassembler.cpp
|
||||
Hash.cpp
|
||||
HttpRequest.cpp
|
||||
|
@ -122,6 +122,7 @@
|
||||
<ClInclude Include="GL\GLInterfaceBase.h" />
|
||||
<ClInclude Include="GL\GLInterface\WGL.h" />
|
||||
<ClInclude Include="GL\GLUtil.h" />
|
||||
<ClInclude Include="FloatUtils.h" />
|
||||
<ClInclude Include="Hash.h" />
|
||||
<ClInclude Include="HttpRequest.h" />
|
||||
<ClInclude Include="IniFile.h" />
|
||||
@ -179,6 +180,7 @@
|
||||
<ClCompile Include="File.cpp" />
|
||||
<ClCompile Include="FileSearch.cpp" />
|
||||
<ClCompile Include="FileUtil.cpp" />
|
||||
<ClCompile Include="FloatUtils.cpp" />
|
||||
<ClCompile Include="GekkoDisassembler.cpp" />
|
||||
<ClCompile Include="GL\GLExtensions\GLExtensions.cpp" />
|
||||
<ClCompile Include="GL\GLInterface\GLInterface.cpp" />
|
||||
|
@ -47,6 +47,7 @@
|
||||
<ClInclude Include="FileUtil.h" />
|
||||
<ClInclude Include="FixedSizeQueue.h" />
|
||||
<ClInclude Include="Flag.h" />
|
||||
<ClInclude Include="FloatUtils.h" />
|
||||
<ClInclude Include="FPURoundMode.h" />
|
||||
<ClInclude Include="Hash.h" />
|
||||
<ClInclude Include="HttpRequest.h" />
|
||||
@ -277,6 +278,7 @@
|
||||
<ClCompile Include="ENetUtil.cpp" />
|
||||
<ClCompile Include="FileSearch.cpp" />
|
||||
<ClCompile Include="FileUtil.cpp" />
|
||||
<ClCompile Include="FloatUtils.cpp" />
|
||||
<ClCompile Include="Hash.cpp" />
|
||||
<ClCompile Include="HttpRequest.cpp" />
|
||||
<ClCompile Include="IniFile.cpp" />
|
||||
|
216
Source/Core/Common/FloatUtils.cpp
Normal file
216
Source/Core/Common/FloatUtils.cpp
Normal file
@ -0,0 +1,216 @@
|
||||
// Copyright 2018 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "Common/FloatUtils.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
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<BaseAndDec, 32> 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<double>::infinity() :
|
||||
std::numeric_limits<double>::infinity();
|
||||
// Special case NaN-ish numbers
|
||||
if (exponent == (0x7FFLL << 52))
|
||||
{
|
||||
if (mantissa == 0)
|
||||
{
|
||||
if (sign)
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return 0.0 + valf;
|
||||
}
|
||||
|
||||
// Negative numbers return NaN
|
||||
if (sign)
|
||||
return std::numeric_limits<double>::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<BaseAndDec, 32> 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<double>::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<float>::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
|
139
Source/Core/Common/FloatUtils.h
Normal file
139
Source/Core/Common/FloatUtils.h
Normal file
@ -0,0 +1,139 @@
|
||||
// Copyright 2018 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <limits>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
namespace Common
|
||||
{
|
||||
template <typename T>
|
||||
constexpr T SNANConstant()
|
||||
{
|
||||
return std::numeric_limits<T>::signaling_NaN();
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
// MSVC needs a workaround, because its std::numeric_limits<double>::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<BaseAndDec, 32> frsqrte_expected;
|
||||
extern const std::array<BaseAndDec, 32> fres_expected;
|
||||
|
||||
// PowerPC approximation algorithms
|
||||
double ApproximateReciprocalSquareRoot(double val);
|
||||
double ApproximateReciprocal(double val);
|
||||
|
||||
} // namespace Common
|
@ -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<BaseAndDec, 32> 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<double>::infinity() :
|
||||
std::numeric_limits<double>::infinity();
|
||||
// Special case NaN-ish numbers
|
||||
if (exponent == (0x7FFLL << 52))
|
||||
{
|
||||
if (mantissa == 0)
|
||||
{
|
||||
if (sign)
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return 0.0 + valf;
|
||||
}
|
||||
|
||||
// Negative numbers return NaN
|
||||
if (sign)
|
||||
return std::numeric_limits<double>::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<BaseAndDec, 32> 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<double>::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<float>::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)
|
||||
|
@ -17,32 +17,6 @@
|
||||
|
||||
namespace MathUtil
|
||||
{
|
||||
template <typename T>
|
||||
constexpr T SNANConstant()
|
||||
{
|
||||
return std::numeric_limits<T>::signaling_NaN();
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
// MSVC needs a workaround, because its std::numeric_limits<double>::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 <class T>
|
||||
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<BaseAndDec, 32> frsqrte_expected;
|
||||
extern const std::array<BaseAndDec, 32> fres_expected;
|
||||
|
||||
// PowerPC approximation algorithms
|
||||
double ApproximateReciprocalSquareRoot(double val);
|
||||
double ApproximateReciprocal(double val);
|
||||
|
||||
template <class T>
|
||||
struct Rectangle
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <limits>
|
||||
|
||||
#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)
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <cmath>
|
||||
|
||||
#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));
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
68
Source/UnitTests/Common/FloatUtilsTest.cpp
Normal file
68
Source/UnitTests/Common/FloatUtilsTest.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright 2018 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <limits>
|
||||
#include <random>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "Common/FloatUtils.h"
|
||||
|
||||
TEST(FloatUtils, IsQNAN)
|
||||
{
|
||||
EXPECT_TRUE(Common::IsQNAN(std::numeric_limits<double>::quiet_NaN()));
|
||||
EXPECT_FALSE(Common::IsQNAN(Common::SNANConstant<double>()));
|
||||
}
|
||||
|
||||
TEST(FloatUtils, IsSNAN)
|
||||
{
|
||||
EXPECT_FALSE(Common::IsSNAN(std::numeric_limits<double>::quiet_NaN()));
|
||||
EXPECT_TRUE(Common::IsSNAN(Common::SNANConstant<double>()));
|
||||
}
|
||||
|
||||
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<float>::denorm_min();
|
||||
volatile double d = std::numeric_limits<double>::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<double>::denorm_min()));
|
||||
EXPECT_EQ(-0.0, Common::FlushToZero(-std::numeric_limits<double>::denorm_min()));
|
||||
EXPECT_EQ(+0.0, Common::FlushToZero(+std::numeric_limits<double>::min() / 2));
|
||||
EXPECT_EQ(-0.0, Common::FlushToZero(-std::numeric_limits<double>::min() / 2));
|
||||
EXPECT_EQ(std::numeric_limits<double>::min(),
|
||||
Common::FlushToZero(std::numeric_limits<double>::min()));
|
||||
EXPECT_EQ(std::numeric_limits<double>::max(),
|
||||
Common::FlushToZero(std::numeric_limits<double>::max()));
|
||||
EXPECT_EQ(+std::numeric_limits<double>::infinity(),
|
||||
Common::FlushToZero(+std::numeric_limits<double>::infinity()));
|
||||
EXPECT_EQ(-std::numeric_limits<double>::infinity(),
|
||||
Common::FlushToZero(-std::numeric_limits<double>::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<u32> 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);
|
||||
}
|
||||
}
|
@ -3,8 +3,6 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <limits>
|
||||
#include <random>
|
||||
|
||||
#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<double>::quiet_NaN()));
|
||||
EXPECT_FALSE(MathUtil::IsQNAN(MathUtil::SNANConstant<double>()));
|
||||
}
|
||||
|
||||
TEST(MathUtil, IsSNAN)
|
||||
{
|
||||
EXPECT_FALSE(MathUtil::IsSNAN(std::numeric_limits<double>::quiet_NaN()));
|
||||
EXPECT_TRUE(MathUtil::IsSNAN(MathUtil::SNANConstant<double>()));
|
||||
}
|
||||
|
||||
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<float>::denorm_min();
|
||||
volatile double d = std::numeric_limits<double>::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<double>::denorm_min()));
|
||||
EXPECT_EQ(-0.0, MathUtil::FlushToZero(-std::numeric_limits<double>::denorm_min()));
|
||||
EXPECT_EQ(+0.0, MathUtil::FlushToZero(+std::numeric_limits<double>::min() / 2));
|
||||
EXPECT_EQ(-0.0, MathUtil::FlushToZero(-std::numeric_limits<double>::min() / 2));
|
||||
EXPECT_EQ(std::numeric_limits<double>::min(),
|
||||
MathUtil::FlushToZero(std::numeric_limits<double>::min()));
|
||||
EXPECT_EQ(std::numeric_limits<double>::max(),
|
||||
MathUtil::FlushToZero(std::numeric_limits<double>::max()));
|
||||
EXPECT_EQ(+std::numeric_limits<double>::infinity(),
|
||||
MathUtil::FlushToZero(+std::numeric_limits<double>::infinity()));
|
||||
EXPECT_EQ(-std::numeric_limits<double>::infinity(),
|
||||
MathUtil::FlushToZero(-std::numeric_limits<double>::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<u32> 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);
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include <gtest/gtest.h> // 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<float, false>());
|
||||
Common::IntFloat expected(val), actual(m_dst.Read<float, false>());
|
||||
if (!actual.f || actual.f != actual.f)
|
||||
EXPECT_EQ(expected.i, actual.i);
|
||||
else
|
||||
|
Loading…
Reference in New Issue
Block a user