Math.Round is implemented as a target intrinsic for Arm64.
JIT emitted `frinta` which rounds the ties to away.
As described in MSDN
https://msdn.microsoft.com/en-us/library/system.math.round(v=vs.110).aspx#Round2_Example,
we should round the ties to even.
So, I corrected `frintn` instruction instead for such intrinsic expansion.
I've also identified that `EvalMathFuncUnary` incorrectly folds the round
operation when the argument is constant at the compile time.
The logic itself is not the right semantic as described above in MSDN.
I made a separate helper function which is essentially a duplicate of
classlib. This is not Arm64 specific fix but applies to all other targets.
So, I just fixed it while I'm here.
Commit migrated from https://github.com/dotnet/coreclr/commit/
75c97cff492584075b6e13d67a901c69c34ad4f9
case CORINFO_INTRINSIC_Round:
genConsumeOperands(treeNode->AsOp());
- getEmitter()->emitInsBinary(INS_frinta, emitTypeSize(treeNode), treeNode, srcNode);
+ getEmitter()->emitInsBinary(INS_frintn, emitTypeSize(treeNode), treeNode, srcNode);
break;
case CORINFO_INTRINSIC_Sqrt:
return u64;
}
+
+// Rounds a double-precision floating-point value to the nearest integer,
+// and rounds midpoint values to the nearest even number.
+// Note this should align with classlib in floatnative.cpp
+// Specializing for x86 using a x87 instruction is optional since
+// this outcome is identical across targets.
+double FloatingPointUtils::round(double d)
+{
+ // If the number has no fractional part do nothing
+ // This shortcut is necessary to workaround precision loss in borderline cases on some platforms
+ if (d == (double)(__int64)d)
+ return d;
+
+ double tempVal = (d + 0.5);
+ //We had a number that was equally close to 2 integers.
+ //We need to return the even one.
+ double flrTempVal = floor(tempVal);
+ if (flrTempVal == tempVal) {
+ if (fmod(tempVal, 2.0) != 0) {
+ flrTempVal -= 1.0;
+ }
+ }
+
+ flrTempVal = _copysign(flrTempVal, d);
+ return flrTempVal;
+}
static float convertUInt64ToFloat(unsigned __int64 u64);
static unsigned __int64 convertDoubleToUInt64(double d);
+
+ static double round(double d);
};
#endif // _UTILS_H_
res = fabs(arg0Val); // The result and params are doubles.
break;
case CORINFO_INTRINSIC_Round:
- res = (arg0Val > 0.0 ? floor(arg0Val + 0.5) : ceil(arg0Val - 0.5));
+ res = FloatingPointUtils::round(arg0Val);
break;
default:
unreached(); // the above are the only math intrinsics at the time of this writing.
WorkingDir=CoreMangLib\cti\system\convert\ConvertToUInt6413
Expected=0
MaxAllowedDurationSeconds=600
-Categories=Pri1;RT;UNSTABLE
+Categories=Pri1;RT;EXPECTED_PASS
HostStyle=0
[ConvertToUInt6414.cmd_6768]
RelativePath=CoreMangLib\cti\system\convert\ConvertToUInt6414\ConvertToUInt6414.cmd
WorkingDir=CoreMangLib\cti\system\math\MathIEEERemainder
Expected=0
MaxAllowedDurationSeconds=600
-Categories=Pri1;RT;EXPECTED_FAIL;DBG_PASS;NEED_TRIAGE
+Categories=Pri1;RT;EXPECTED_PASS
HostStyle=0
[MathLog.cmd_7307]
RelativePath=CoreMangLib\cti\system\math\MathLog\MathLog.cmd
WorkingDir=CoreMangLib\cti\system\math\MathRound2
Expected=0
MaxAllowedDurationSeconds=600
-Categories=Pri1;RT;EXPECTED_FAIL;NEED_TRIAGE;DBG_PASS
+Categories=Pri1;RT;EXPECTED_PASS
HostStyle=0
[MathRound3.cmd_7335]
RelativePath=CoreMangLib\cti\system\math\MathRound3\MathRound3.cmd
WorkingDir=CoreMangLib\cti\system\math\MathRound4
Expected=0
MaxAllowedDurationSeconds=600
-Categories=Pri1;RT;EXPECTED_FAIL;NEED_TRIAGE;DBG_PASS
+Categories=Pri1;RT;EXPECTED_PASS
HostStyle=0
[MathSign1.cmd_7337]
RelativePath=CoreMangLib\cti\system\math\MathSign1\MathSign1.cmd