Skip to content

Improve min max performance and IEEE compliance #2810

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Nov 9, 2023
Merged
8 changes: 4 additions & 4 deletions src/CLR/System.Math/nf_native_system_math.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ static const CLR_RT_MethodHandler method_lookup[] =
Library_nf_native_system_math_System_Math::Log10___STATIC__R8__R8,
NULL,
Library_nf_native_system_math_System_Math::Max___STATIC__R8__R8__R8,
NULL,
Library_nf_native_system_math_System_Math::Max___STATIC__R4__R4__R4,
NULL,
Library_nf_native_system_math_System_Math::Min___STATIC__R8__R8__R8,
NULL,
Library_nf_native_system_math_System_Math::Min___STATIC__R4__R4__R4,
Library_nf_native_system_math_System_Math::Pow___STATIC__R8__R8__R8,
Library_nf_native_system_math_System_Math::Round___STATIC__R8__R8,
Library_nf_native_system_math_System_Math::Sign___STATIC__I4__R8,
Expand All @@ -50,9 +50,9 @@ static const CLR_RT_MethodHandler method_lookup[] =
const CLR_RT_NativeAssemblyData g_CLR_AssemblyNative_System_Math =
{
"System.Math",
0x46092CB1,
0x9F9E2A7E,
method_lookup,
{ 100, 0, 5, 4 }
{ 100, 0, 5, 5 }
};

// clang-format on
2 changes: 2 additions & 0 deletions src/CLR/System.Math/nf_native_system_math.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ struct Library_nf_native_system_math_System_Math
NANOCLR_NATIVE_DECLARE(Log___STATIC__R8__R8);
NANOCLR_NATIVE_DECLARE(Log10___STATIC__R8__R8);
NANOCLR_NATIVE_DECLARE(Max___STATIC__R8__R8__R8);
NANOCLR_NATIVE_DECLARE(Max___STATIC__R4__R4__R4);
NANOCLR_NATIVE_DECLARE(Min___STATIC__R8__R8__R8);
NANOCLR_NATIVE_DECLARE(Min___STATIC__R4__R4__R4);
NANOCLR_NATIVE_DECLARE(Pow___STATIC__R8__R8__R8);
NANOCLR_NATIVE_DECLARE(Round___STATIC__R8__R8);
NANOCLR_NATIVE_DECLARE(Sign___STATIC__I4__R8);
Expand Down
188 changes: 166 additions & 22 deletions src/CLR/System.Math/nf_native_system_math_System_Math.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,37 +13,55 @@ HRESULT Library_nf_native_system_math_System_Math::Max___STATIC__R8__R8__R8(CLR_
NATIVE_PROFILE_CLR_CORE();
NANOCLR_HEADER();

// This matches the IEEE 754:2019 `maximum` function
//
// It propagates NaN inputs back to the caller and
// otherwise returns the greater of the inputs. It
// treats +0 as greater than -0 as per the specification.

#if (DP_FLOATINGPOINT == TRUE)

double x = stack.Arg0().NumericByRefConst().r8;
double y = stack.Arg1().NumericByRefConst().r8;
double val1 = stack.Arg0().NumericByRefConst().r8;
double val2 = stack.Arg1().NumericByRefConst().r8;

// from .NET spec: If val1, val2, or both val1 and val2 are equal to NaN, NaN is returned.
if (System::Double::IsNaN(x) || System::Double::IsNaN(y))
if (val1 != val2)
{
stack.SetResult_R8(NAN);
if (!__isnand(val1))
{
double res = val2 < val1 ? val1 : val2;
stack.SetResult_R8(res);
}
else
{
stack.SetResult_R8(val1);
}
}
else
{
double res = x >= y ? x : y;

double res = (__signbitd(val2) != 0) ? val1 : val2;
stack.SetResult_R8(res);
}

#else

float x = (float)stack.Arg0().NumericByRefConst().r8;
float y = (float)stack.Arg1().NumericByRefConst().r8;
float val1 = (float)stack.Arg0().NumericByRefConst().r8;
float val2 = (float)stack.Arg1().NumericByRefConst().r8;

// from .NET spec: If val1, val2, or both val1 and val2 are equal to NaN, NaN is returned.
if (System::Double::IsNaN(x) || System::Double::IsNaN(y))
if (val1 != val2)
{
stack.SetResult_R8(NAN);
if (!__isnand(val1))
{
float res = val2 < val1 ? val1 : val2;
stack.SetResult_R8(res);
}
else
{
stack.SetResult_R8(val1);
}
}
else
{
float res = x >= y ? x : y;

float res = (__signbitd(val2) != 0) ? val1 : val2;
stack.SetResult_R8(res);
}

Expand All @@ -52,32 +70,138 @@ HRESULT Library_nf_native_system_math_System_Math::Max___STATIC__R8__R8__R8(CLR_
NANOCLR_NOCLEANUP_NOLABEL();
}

HRESULT Library_nf_native_system_math_System_Math::Max___STATIC__R4__R4__R4(CLR_RT_StackFrame &stack)
{
NATIVE_PROFILE_CLR_CORE();
NANOCLR_HEADER();

// This matches the IEEE 754:2019 `maximum` function
//
// It propagates NaN inputs back to the caller and
// otherwise returns the greater of the inputs. It
// treats +0 as greater than -0 as per the specification.

float val1 = (float)stack.Arg0().NumericByRefConst().r4;
float val2 = (float)stack.Arg1().NumericByRefConst().r4;

if (val1 != val2)
{
if (!__isnand(val1))
{
float res = val2 < val1 ? val1 : val2;
stack.SetResult_R4(res);
}
else
{
stack.SetResult_R4(val1);
}
}
else
{
float res = (__signbitd(val2) != 0) ? val1 : val2;
stack.SetResult_R4(res);
}

NANOCLR_NOCLEANUP_NOLABEL();
}

HRESULT Library_nf_native_system_math_System_Math::Min___STATIC__R8__R8__R8(CLR_RT_StackFrame &stack)
{
NATIVE_PROFILE_CLR_CORE();
NANOCLR_HEADER();

// This matches the IEEE 754:2019 `minimum` function
//
// It propagates NaN inputs back to the caller and
// otherwise returns the lesser of the inputs. It
// treats +0 as lesser than -0 as per the specification.

#if (DP_FLOATINGPOINT == TRUE)

double x = stack.Arg0().NumericByRefConst().r8;
double y = stack.Arg1().NumericByRefConst().r8;
double res = x <= y ? x : y;
double val1 = stack.Arg0().NumericByRefConst().r8;
double val2 = stack.Arg1().NumericByRefConst().r8;

stack.SetResult_R8(res);
if (val1 != val2)
{
if (!__isnand(val1))
{
double res = val1 < val2 ? val1 : val2;
stack.SetResult_R8(res);
}
else
{
stack.SetResult_R8(val1);
}
}
else
{
double res = (__signbitd(val2) != 0) ? val1 : val2;
stack.SetResult_R8(res);
}

#else

float x = (float)stack.Arg0().NumericByRefConst().r8;
float y = (float)stack.Arg1().NumericByRefConst().r8;
float res = x <= y ? x : y;
float val1 = (float)stack.Arg0().NumericByRefConst().r8;
float val2 = (float)stack.Arg1().NumericByRefConst().r8;

stack.SetResult_R8(res);
if (val1 != val2)
{
if (!__isnand(val1))
{
float res = val1 < val2 ? val1 : val2;
stack.SetResult_R8(res);
}
else
{
stack.SetResult_R8((float)val1);
}
}
else
{
float res = (__signbitd(val2) != 0) ? val1 : val2;
stack.SetResult_R8(res);
}

#endif

NANOCLR_NOCLEANUP_NOLABEL();
}

HRESULT Library_nf_native_system_math_System_Math::Min___STATIC__R4__R4__R4(CLR_RT_StackFrame &stack)
{
NATIVE_PROFILE_CLR_CORE();
NANOCLR_HEADER();

// This matches the IEEE 754:2019 `minimum` function
//
// It propagates NaN inputs back to the caller and
// otherwise returns the lesser of the inputs. It
// treats +0 as lesser than -0 as per the specification.

float val1 = (float)stack.Arg0().NumericByRefConst().r4;
float val2 = (float)stack.Arg1().NumericByRefConst().r4;

if (val1 != val2)
{
if (!__isnand(val1))
{
float res = val1 < val2 ? val1 : val2;
stack.SetResult_R4(res);
}
else
{
stack.SetResult_R4(val1);
}
}
else
{
float res = (__signbitd(val2) != 0) ? val1 : val2;
stack.SetResult_R8(res);
}

NANOCLR_NOCLEANUP_NOLABEL();
}

HRESULT Library_nf_native_system_math_System_Math::Abs___STATIC__R8__R8(CLR_RT_StackFrame &stack)
{
NATIVE_PROFILE_CLR_CORE();
Expand Down Expand Up @@ -820,6 +944,16 @@ HRESULT Library_nf_native_system_math_System_Math::Max___STATIC__R8__R8__R8(CLR_
NANOCLR_NOCLEANUP();
}

HRESULT Library_nf_native_system_math_System_Math::Max___STATIC__R4__R4__R4(CLR_RT_StackFrame &stack)
{
NATIVE_PROFILE_CLR_CORE();
NANOCLR_HEADER();

NANOCLR_SET_AND_LEAVE(stack.NotImplementedStub());

NANOCLR_NOCLEANUP();
}

HRESULT Library_nf_native_system_math_System_Math::Min___STATIC__R8__R8__R8(CLR_RT_StackFrame &stack)
{
NATIVE_PROFILE_CLR_CORE();
Expand All @@ -830,6 +964,16 @@ HRESULT Library_nf_native_system_math_System_Math::Min___STATIC__R8__R8__R8(CLR_
NANOCLR_NOCLEANUP();
}

HRESULT Library_nf_native_system_math_System_Math::Min___STATIC__R4__R4__R4(CLR_RT_StackFrame &stack)
{
NATIVE_PROFILE_CLR_CORE();
NANOCLR_HEADER();

NANOCLR_SET_AND_LEAVE(stack.NotImplementedStub());

NANOCLR_NOCLEANUP();
}

HRESULT Library_nf_native_system_math_System_Math::Abs___STATIC__R8__R8(CLR_RT_StackFrame &stack)
{
NATIVE_PROFILE_CLR_CORE();
Expand Down