Interpreter: Unset FPSCR.FI and FPSCR.FR for QNaN and infinity input operands

This hardware behavior makes sense, as the FI bit is used to signify an
inexact result. An inexact result is a form of value that results during
the rounding phase of denormalization. If any bits of the significand
are lost during said rounding, then the result is considered to be
inexact.

However NaN and infinity are not classed as subnormals and therefore
don't undergo the denormalization step, making loss of precision not
possible (in NaN's case, numerically rounding something that is
literally Not a Number doesn't even make sense).

FR is set to indicate whether or not the last arithmetic or rounding and
conversion instruction that rounded the intermediate result incremented
the fractional portion of the result. Given neither input types would be
affected by this, this should also be unset.

This corrects more of the exceptional case handling for these values to
match hardware.
This commit is contained in:
Lioncash 2018-06-03 16:45:20 -04:00
parent c22205cd7e
commit 9068109b3e
3 changed files with 42 additions and 42 deletions

View File

@ -103,10 +103,9 @@ inline double NI_mul(double a, double b)
if (std::isnan(t)) if (std::isnan(t))
{ {
if (Common::IsSNAN(a) || Common::IsSNAN(b)) if (Common::IsSNAN(a) || Common::IsSNAN(b))
{
SetFPException(FPSCR_VXSNAN); SetFPException(FPSCR_VXSNAN);
FPSCR.ClearFIFR();
} FPSCR.ClearFIFR();
if (std::isnan(a)) if (std::isnan(a))
return MakeQuiet(a); return MakeQuiet(a);
@ -114,7 +113,6 @@ inline double NI_mul(double a, double b)
return MakeQuiet(b); return MakeQuiet(b);
SetFPException(FPSCR_VXIMZ); SetFPException(FPSCR_VXIMZ);
FPSCR.ClearFIFR();
return PPC_NAN; return PPC_NAN;
} }
return t; return t;
@ -127,10 +125,9 @@ inline double NI_div(double a, double b)
if (std::isnan(t)) if (std::isnan(t))
{ {
if (Common::IsSNAN(a) || Common::IsSNAN(b)) if (Common::IsSNAN(a) || Common::IsSNAN(b))
{
SetFPException(FPSCR_VXSNAN); SetFPException(FPSCR_VXSNAN);
FPSCR.ClearFIFR();
} FPSCR.ClearFIFR();
if (std::isnan(a)) if (std::isnan(a))
return MakeQuiet(a); return MakeQuiet(a);
@ -142,18 +139,15 @@ inline double NI_div(double a, double b)
if (a == 0.0) if (a == 0.0)
{ {
SetFPException(FPSCR_VXZDZ); SetFPException(FPSCR_VXZDZ);
FPSCR.ClearFIFR();
} }
else else
{ {
SetFPException(FPSCR_ZX); SetFPException(FPSCR_ZX);
FPSCR.ClearFIFR();
} }
} }
else if (std::isinf(a) && std::isinf(b)) else if (std::isinf(a) && std::isinf(b))
{ {
SetFPException(FPSCR_VXIDI); SetFPException(FPSCR_VXIDI);
FPSCR.ClearFIFR();
} }
return PPC_NAN; return PPC_NAN;
@ -169,10 +163,9 @@ inline double NI_add(double a, double b)
if (std::isnan(t)) if (std::isnan(t))
{ {
if (Common::IsSNAN(a) || Common::IsSNAN(b)) if (Common::IsSNAN(a) || Common::IsSNAN(b))
{
SetFPException(FPSCR_VXSNAN); SetFPException(FPSCR_VXSNAN);
FPSCR.ClearFIFR();
} FPSCR.ClearFIFR();
if (std::isnan(a)) if (std::isnan(a))
return MakeQuiet(a); return MakeQuiet(a);
@ -180,10 +173,12 @@ inline double NI_add(double a, double b)
return MakeQuiet(b); return MakeQuiet(b);
SetFPException(FPSCR_VXISI); SetFPException(FPSCR_VXISI);
FPSCR.ClearFIFR();
return PPC_NAN; return PPC_NAN;
} }
if (std::isinf(a) || std::isinf(b))
FPSCR.ClearFIFR();
return t; return t;
} }
@ -194,10 +189,9 @@ inline double NI_sub(double a, double b)
if (std::isnan(t)) if (std::isnan(t))
{ {
if (Common::IsSNAN(a) || Common::IsSNAN(b)) if (Common::IsSNAN(a) || Common::IsSNAN(b))
{
SetFPException(FPSCR_VXSNAN); SetFPException(FPSCR_VXSNAN);
FPSCR.ClearFIFR();
} FPSCR.ClearFIFR();
if (std::isnan(a)) if (std::isnan(a))
return MakeQuiet(a); return MakeQuiet(a);
@ -205,10 +199,12 @@ inline double NI_sub(double a, double b)
return MakeQuiet(b); return MakeQuiet(b);
SetFPException(FPSCR_VXISI); SetFPException(FPSCR_VXISI);
FPSCR.ClearFIFR();
return PPC_NAN; return PPC_NAN;
} }
if (std::isinf(a) || std::isinf(b))
FPSCR.ClearFIFR();
return t; return t;
} }
@ -222,10 +218,9 @@ inline double NI_madd(double a, double c, double b)
if (std::isnan(t)) if (std::isnan(t))
{ {
if (Common::IsSNAN(a) || Common::IsSNAN(b) || Common::IsSNAN(c)) if (Common::IsSNAN(a) || Common::IsSNAN(b) || Common::IsSNAN(c))
{
SetFPException(FPSCR_VXSNAN); SetFPException(FPSCR_VXSNAN);
FPSCR.ClearFIFR();
} FPSCR.ClearFIFR();
if (std::isnan(a)) if (std::isnan(a))
return MakeQuiet(a); return MakeQuiet(a);
@ -235,7 +230,6 @@ inline double NI_madd(double a, double c, double b)
return MakeQuiet(c); return MakeQuiet(c);
SetFPException(FPSCR_VXIMZ); SetFPException(FPSCR_VXIMZ);
FPSCR.ClearFIFR();
return PPC_NAN; return PPC_NAN;
} }
@ -244,19 +238,20 @@ inline double NI_madd(double a, double c, double b)
if (std::isnan(t)) if (std::isnan(t))
{ {
if (Common::IsSNAN(b)) if (Common::IsSNAN(b))
{
SetFPException(FPSCR_VXSNAN); SetFPException(FPSCR_VXSNAN);
FPSCR.ClearFIFR();
} FPSCR.ClearFIFR();
if (std::isnan(b)) if (std::isnan(b))
return MakeQuiet(b); return MakeQuiet(b);
SetFPException(FPSCR_VXISI); SetFPException(FPSCR_VXISI);
FPSCR.ClearFIFR();
return PPC_NAN; return PPC_NAN;
} }
if (std::isinf(a) || std::isinf(b) || std::isinf(c))
FPSCR.ClearFIFR();
return t; return t;
} }
@ -267,10 +262,9 @@ inline double NI_msub(double a, double c, double b)
if (std::isnan(t)) if (std::isnan(t))
{ {
if (Common::IsSNAN(a) || Common::IsSNAN(b) || Common::IsSNAN(c)) if (Common::IsSNAN(a) || Common::IsSNAN(b) || Common::IsSNAN(c))
{
SetFPException(FPSCR_VXSNAN); SetFPException(FPSCR_VXSNAN);
FPSCR.ClearFIFR();
} FPSCR.ClearFIFR();
if (std::isnan(a)) if (std::isnan(a))
return MakeQuiet(a); return MakeQuiet(a);
@ -280,7 +274,6 @@ inline double NI_msub(double a, double c, double b)
return MakeQuiet(c); return MakeQuiet(c);
SetFPException(FPSCR_VXIMZ); SetFPException(FPSCR_VXIMZ);
FPSCR.ClearFIFR();
return PPC_NAN; return PPC_NAN;
} }
@ -289,19 +282,20 @@ inline double NI_msub(double a, double c, double b)
if (std::isnan(t)) if (std::isnan(t))
{ {
if (Common::IsSNAN(b)) if (Common::IsSNAN(b))
{
SetFPException(FPSCR_VXSNAN); SetFPException(FPSCR_VXSNAN);
FPSCR.ClearFIFR();
} FPSCR.ClearFIFR();
if (std::isnan(b)) if (std::isnan(b))
return MakeQuiet(b); return MakeQuiet(b);
SetFPException(FPSCR_VXISI); SetFPException(FPSCR_VXISI);
FPSCR.ClearFIFR();
return PPC_NAN; return PPC_NAN;
} }
if (std::isinf(a) || std::isinf(b) || std::isinf(c))
FPSCR.ClearFIFR();
return t; return t;
} }

View File

@ -409,6 +409,9 @@ void Interpreter::fresx(UGeckoInstruction inst)
} }
else else
{ {
if (std::isnan(b) || std::isinf(b))
FPSCR.ClearFIFR();
compute_result(b); compute_result(b);
} }
@ -452,6 +455,9 @@ void Interpreter::frsqrtex(UGeckoInstruction inst)
} }
else else
{ {
if (std::isnan(b) || std::isinf(b))
FPSCR.ClearFIFR();
compute_result(b); compute_result(b);
} }

View File

@ -124,11 +124,11 @@ void Interpreter::ps_res(UGeckoInstruction inst)
FPSCR.ClearFIFR(); FPSCR.ClearFIFR();
} }
if (Common::IsSNAN(a) || Common::IsSNAN(b)) if (std::isnan(a) || std::isinf(a) || std::isnan(b) || std::isinf(b))
{
SetFPException(FPSCR_VXSNAN);
FPSCR.ClearFIFR(); FPSCR.ClearFIFR();
}
if (Common::IsSNAN(a) || Common::IsSNAN(b))
SetFPException(FPSCR_VXSNAN);
rPS0(inst.FD) = Common::ApproximateReciprocal(a); rPS0(inst.FD) = Common::ApproximateReciprocal(a);
rPS1(inst.FD) = Common::ApproximateReciprocal(b); rPS1(inst.FD) = Common::ApproximateReciprocal(b);
@ -155,11 +155,11 @@ void Interpreter::ps_rsqrte(UGeckoInstruction inst)
FPSCR.ClearFIFR(); FPSCR.ClearFIFR();
} }
if (Common::IsSNAN(ps0) || Common::IsSNAN(ps1)) if (std::isnan(ps0) || std::isinf(ps0) || std::isnan(ps1) || std::isinf(ps1))
{
SetFPException(FPSCR_VXSNAN);
FPSCR.ClearFIFR(); FPSCR.ClearFIFR();
}
if (Common::IsSNAN(ps0) || Common::IsSNAN(ps1))
SetFPException(FPSCR_VXSNAN);
rPS0(inst.FD) = ForceSingle(Common::ApproximateReciprocalSquareRoot(ps0)); rPS0(inst.FD) = ForceSingle(Common::ApproximateReciprocalSquareRoot(ps0));
rPS1(inst.FD) = ForceSingle(Common::ApproximateReciprocalSquareRoot(ps1)); rPS1(inst.FD) = ForceSingle(Common::ApproximateReciprocalSquareRoot(ps1));