-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Fixing the handling of Positive NaN in Math.Min for float/double #70795
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
Changes from all commits
8a68b3e
3bfa000
2242d42
819387f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -888,8 +888,8 @@ public static double Max(double val1, double val2) | |
| // This matches the IEEE 754:2019 `maximum` function | ||
| // | ||
| // It propagates NaN inputs back to the caller and | ||
| // otherwise returns the larger of the inputs. It | ||
| // treats +0 as larger than -0 as per the specification. | ||
| // otherwise returns the greater of the inputs. It | ||
| // treats +0 as greater than -0 as per the specification. | ||
|
|
||
| if (val1 != val2) | ||
| { | ||
|
|
@@ -946,8 +946,8 @@ public static float Max(float val1, float val2) | |
| // This matches the IEEE 754:2019 `maximum` function | ||
| // | ||
| // It propagates NaN inputs back to the caller and | ||
| // otherwise returns the larger of the inputs. It | ||
| // treats +0 as larger than -0 as per the specification. | ||
| // otherwise returns the greater of the inputs. It | ||
| // treats +0 as greater than -0 as per the specification. | ||
|
|
||
| if (val1 != val2) | ||
| { | ||
|
|
@@ -999,8 +999,8 @@ public static double MaxMagnitude(double x, double y) | |
| // This matches the IEEE 754:2019 `maximumMagnitude` function | ||
| // | ||
| // It propagates NaN inputs back to the caller and | ||
| // otherwise returns the input with a larger magnitude. | ||
| // It treats +0 as larger than -0 as per the specification. | ||
| // otherwise returns the input with a greater magnitude. | ||
| // It treats +0 as greater than -0 as per the specification. | ||
|
|
||
| double ax = Abs(x); | ||
| double ay = Abs(y); | ||
|
|
@@ -1037,12 +1037,17 @@ public static double Min(double val1, double val2) | |
| // This matches the IEEE 754:2019 `minimum` function | ||
| // | ||
| // It propagates NaN inputs back to the caller and | ||
| // otherwise returns the larger of the inputs. It | ||
| // treats +0 as larger than -0 as per the specification. | ||
| // otherwise returns the lesser of the inputs. It | ||
| // treats +0 as lesser than -0 as per the specification. | ||
|
|
||
| if (val1 != val2 && !double.IsNaN(val1)) | ||
| if (val1 != val2) | ||
| { | ||
| return val1 < val2 ? val1 : val2; | ||
| if (!double.IsNaN(val1)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just curious, when we write C# that's potentially in a hot path is there any value in putting "most likely branch first" (ie., "test is most likely true")? This would mean that the codegen will maintain this ordering + the CPU in the absence of other information will assume the branch is true + this isn't overwhelmed by accurate subsequent branch prediction? I only ask because I'd personally find something like this easier to grok if the double.IsNaN was not negated.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's why the code is how it is. With the way the C# emits IL and the JIT compiles it, then by default (that is when no PGO data is present), the code is effectively: if (cond)
{
// predicted
}
// unpredictedIt's the opposite for ternaries: This ordering ensures that the most predicted path is the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting. Why are ternaries the reverse of what I'd expect they'd be if they were translated into "if/else"? Ideally we'd have POGO for anything "hot" so we didn't have to write our code like this. Curious whether @AndyAyersMS @stephentoub have any color to add here
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the BCL is too large to have accurate PGO data for many of these functions, there are likewise cases where PGO data will mess up or hinder normal workflows. Many of the These functions are actually prime examples of where we probably (longer term) want to make them branchless by default (using SIMD and/or Conditional Instructions) and where we'd only change that with dynamic PGO that indicates otherwise for a particular call-site.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would recommend in most cases writing the code whatever way seems clearest and not worrying about this level of detail. There is no guarantee the jit will maintain IL order for emitted code.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The BCL is filled with code that cares about ordering, especially in the hottest functions (such as for I expect if the JIT ever changes that we will have a large number of regressions in the perf triage and a higher push for JIT hints to specify static data around "likely" vs "unlikely".
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We change the code layout and branch optimization logic in the jit pretty frequently and more changes are in the plans for the future. I don't know how to reconcile that with what you are saying.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We make tweaks yes, but even then the JIT typically defaults to (barring PGO data telling it otherwise), matching the order so that Changing this causes the JIT to do more work because it either has to insert more branches or rearrange blocks and that's not worth doing unless we have data (like PGO) indicating otherwise. Functions like |
||
| { | ||
| return val1 < val2 ? val1 : val2; | ||
| } | ||
|
|
||
| return val1; | ||
| } | ||
|
|
||
| return double.IsNegative(val1) ? val1 : val2; | ||
|
|
@@ -1090,12 +1095,17 @@ public static float Min(float val1, float val2) | |
| // This matches the IEEE 754:2019 `minimum` function | ||
| // | ||
| // It propagates NaN inputs back to the caller and | ||
| // otherwise returns the larger of the inputs. It | ||
| // treats +0 as larger than -0 as per the specification. | ||
| // otherwise returns the lesser of the inputs. It | ||
| // treats +0 as lesser than -0 as per the specification. | ||
|
|
||
| if (val1 != val2 && !float.IsNaN(val1)) | ||
| if (val1 != val2) | ||
| { | ||
| return val1 < val2 ? val1 : val2; | ||
| if (!float.IsNaN(val1)) | ||
| { | ||
| return val1 < val2 ? val1 : val2; | ||
| } | ||
|
|
||
| return val1; | ||
| } | ||
|
|
||
| return float.IsNegative(val1) ? val1 : val2; | ||
|
|
@@ -1138,8 +1148,8 @@ public static double MinMagnitude(double x, double y) | |
| // This matches the IEEE 754:2019 `minimumMagnitude` function | ||
| // | ||
| // It propagates NaN inputs back to the caller and | ||
| // otherwise returns the input with a larger magnitude. | ||
| // It treats +0 as larger than -0 as per the specification. | ||
| // otherwise returns the input with a lesser magnitude. | ||
| // It treats +0 as lesser than -0 as per the specification. | ||
|
|
||
| double ax = Abs(x); | ||
| double ay = Abs(y); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.