FPU NaN and negative zero fixes. Verified against the real hardware.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@4847 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
skidau 2010-01-16 03:43:16 +00:00
parent c62fb1d7d2
commit b3b7ed2a2f
3 changed files with 36 additions and 30 deletions

View File

@ -24,7 +24,7 @@
using namespace MathUtil; using namespace MathUtil;
// warining! very slow! // warning! very slow! This setting fixes NAN
//#define VERY_ACCURATE_FP //#define VERY_ACCURATE_FP
#define MIN_SINGLE 0xc7efffffe0000000ull #define MIN_SINGLE 0xc7efffffe0000000ull
@ -188,26 +188,31 @@ inline double NI_madd(const double a, const double b, const double c)
inline double NI_msub(const double a, const double b, const double c) inline double NI_msub(const double a, const double b, const double c)
{ {
#ifdef VERY_ACCURATE_FP //#ifdef VERY_ACCURATE_FP
if (a != a) return a; // This code does not produce accurate fp! NAN's are not calculated correctly, nor negative zero.
if (c != c) return c; // The code is kept here for reference.
if (b != b) return b; //
double t = a * b; // if (a != a) return a;
if (t != t) // if (c != c) return c;
{ // if (b != b) return b;
SetFPException(FPSCR_VXIMZ); // double t = a * b;
return PPC_NAN; // if (t != t)
} // {
t = t - c; // SetFPException(FPSCR_VXIMZ);
if (t != t) // return PPC_NAN;
{ // }
SetFPException(FPSCR_VXISI); //
return PPC_NAN; // t = t - c;
} // if (t != t)
return t; // {
#else // SetFPException(FPSCR_VXISI);
// return PPC_NAN;
// }
// return t;
//#else
// This code does not calculate QNAN's correctly but calculates negative zero correctly.
return NI_sub(NI_mul(a, b), c); return NI_sub(NI_mul(a, b), c);
#endif // #endif
} }
// used by stfsXX instructions and ps_rsqrte // used by stfsXX instructions and ps_rsqrte

View File

@ -436,27 +436,28 @@ void fmsubsx(UGeckoInstruction _inst)
if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD));
} }
void fnmaddx(UGeckoInstruction _inst) void fnmaddx(UGeckoInstruction _inst)
{ {
rPS0(_inst.FD) = ForceDouble(0.0-NI_madd(rPS0(_inst.FA), rPS0(_inst.FC), rPS0(_inst.FB))); rPS0(_inst.FD) = ForceDouble(-NI_madd(rPS0(_inst.FA), rPS0(_inst.FC), rPS0(_inst.FB)));
UpdateFPRF(rPS0(_inst.FD)); UpdateFPRF(rPS0(_inst.FD));
if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD));
} }
void fnmaddsx(UGeckoInstruction _inst) void fnmaddsx(UGeckoInstruction _inst)
{ {
rPS0(_inst.FD) = rPS1(_inst.FD) = rPS0(_inst.FD) = rPS1(_inst.FD) =
ForceSingle(0.0-NI_madd(rPS0(_inst.FA), rPS0(_inst.FC), rPS0(_inst.FB))); ForceSingle(-NI_madd(rPS0(_inst.FA), rPS0(_inst.FC), rPS0(_inst.FB)));
UpdateFPRF(rPS0(_inst.FD)); UpdateFPRF(rPS0(_inst.FD));
if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD));
} }
void fnmsubx(UGeckoInstruction _inst) void fnmsubx(UGeckoInstruction _inst)
{ {
rPS0(_inst.FD) = ForceDouble(0.0-NI_msub(rPS0(_inst.FA), rPS0(_inst.FC), rPS0(_inst.FB))); rPS0(_inst.FD) = ForceDouble(-NI_msub(rPS0(_inst.FA), rPS0(_inst.FC), rPS0(_inst.FB)));
UpdateFPRF(rPS0(_inst.FD)); UpdateFPRF(rPS0(_inst.FD));
if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD));
} }
// fnmsubsx does not handle QNAN properly - see NI_msub
void fnmsubsx(UGeckoInstruction _inst) void fnmsubsx(UGeckoInstruction _inst)
{ {
rPS0(_inst.FD) = rPS1(_inst.FD) = rPS0(_inst.FD) = rPS1(_inst.FD) =
@ -465,13 +466,13 @@ void fnmsubsx(UGeckoInstruction _inst)
if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD));
} }
void fsubx(UGeckoInstruction _inst) void fsubx(UGeckoInstruction _inst)
{ {
rPS0(_inst.FD) = ForceDouble(NI_sub(rPS0(_inst.FA), rPS0(_inst.FB))); rPS0(_inst.FD) = ForceDouble(NI_sub(rPS0(_inst.FA), rPS0(_inst.FB)));
UpdateFPRF(rPS0(_inst.FD)); UpdateFPRF(rPS0(_inst.FD));
if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD));
} }
void fsubsx(UGeckoInstruction _inst) void fsubsx(UGeckoInstruction _inst)
{ {
rPS0(_inst.FD) = rPS1(_inst.FD) = ForceSingle(NI_sub(rPS0(_inst.FA), rPS0(_inst.FB))); rPS0(_inst.FD) = rPS1(_inst.FD) = ForceSingle(NI_sub(rPS0(_inst.FA), rPS0(_inst.FB)));

View File

@ -296,16 +296,16 @@ void ps_madd(UGeckoInstruction _inst)
void ps_nmsub(UGeckoInstruction _inst) void ps_nmsub(UGeckoInstruction _inst)
{ {
rPS0(_inst.FD) = ForceSingle( 0.0-NI_msub( rPS0(_inst.FA), rPS0(_inst.FC), rPS0(_inst.FB) ) ); rPS0(_inst.FD) = ForceSingle( -NI_msub( rPS0(_inst.FA), rPS0(_inst.FC), rPS0(_inst.FB) ) );
rPS1(_inst.FD) = ForceSingle( 0.0-NI_msub( rPS1(_inst.FA), rPS1(_inst.FC), rPS1(_inst.FB) ) ); rPS1(_inst.FD) = ForceSingle( -NI_msub( rPS1(_inst.FA), rPS1(_inst.FC), rPS1(_inst.FB) ) );
UpdateFPRF(rPS0(_inst.FD)); UpdateFPRF(rPS0(_inst.FD));
if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD));
} }
void ps_nmadd(UGeckoInstruction _inst) void ps_nmadd(UGeckoInstruction _inst)
{ {
rPS0(_inst.FD) = ForceSingle( 0.0-NI_madd( rPS0(_inst.FA), rPS0(_inst.FC), rPS0(_inst.FB) ) ); rPS0(_inst.FD) = ForceSingle( -NI_madd( rPS0(_inst.FA), rPS0(_inst.FC), rPS0(_inst.FB) ) );
rPS1(_inst.FD) = ForceSingle( 0.0-NI_madd( rPS1(_inst.FA), rPS1(_inst.FC), rPS1(_inst.FB) ) ); rPS1(_inst.FD) = ForceSingle( -NI_madd( rPS1(_inst.FA), rPS1(_inst.FC), rPS1(_inst.FB) ) );
UpdateFPRF(rPS0(_inst.FD)); UpdateFPRF(rPS0(_inst.FD));
if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD));
} }