{
// If the number has no fractional part do nothing
// This shortcut is necessary to workaround precision loss in borderline cases on some platforms
- if (x == ((double)((__int64)x)))
+ if (x == (double)((INT64)x))
{
return x;
}
return _copysign(flrTempVal, x);
}
+
+// Windows x86 and Windows ARM/ARM64 may not define _copysignf() but they do define _copysign().
+// We will redirect the macro to this other functions if the macro is not defined for the platform.
+// This has the side effect of a possible implicit upcasting for arguments passed in and an explicit
+// downcasting for the _copysign() call.
+#if (defined(_TARGET_X86_) || defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)) && !defined(FEATURE_PAL)
+
+#if !defined(_copysignf)
+#define _copysignf (float)_copysign
+#endif
+
+#endif
+
+// Rounds a single-precision floating-point value to the nearest integer,
+// and rounds midpoint values to the nearest even number.
+// Note this should align with classlib in floatsingle.cpp
+// Specializing for x86 using a x87 instruction is optional since
+// this outcome is identical across targets.
+float FloatingPointUtils::round(float x)
+{
+ // If the number has no fractional part do nothing
+ // This shortcut is necessary to workaround precision loss in borderline cases on some platforms
+ if (x == (float)((INT32)x))
+ {
+ return x;
+ }
+
+ // We had a number that was equally close to 2 integers.
+ // We need to return the even one.
+
+ float tempVal = (x + 0.5f);
+ float flrTempVal = floorf(tempVal);
+
+ if ((flrTempVal == tempVal) && (fmodf(tempVal, 2.0f) != 0))
+ {
+ flrTempVal -= 1.0f;
+ }
+
+ return _copysignf(flrTempVal, x);
+}
{
assert(IsVNConstant(argVN));
var_types argVNtyp = TypeOfVN(argVN);
+ assert(varTypeIsFloating(argVNtyp));
double result = 0;
return result;
}
+// Given a float constant value number return its value as a float.
+//
+float ValueNumStore::GetConstantSingle(ValueNum argVN)
+{
+ assert(IsVNConstant(argVN));
+ var_types argVNtyp = TypeOfVN(argVN);
+ assert(argVNtyp == TYP_FLOAT);
+
+ float result = 0;
+
+ switch (argVNtyp)
+ {
+ case TYP_FLOAT:
+ result = ConstantValue<float>(argVN);
+ break;
+ default:
+ unreached();
+ }
+ return result;
+}
+
// Compute the proper value number when the VNFunc has all constant arguments
// This essentially performs constant folding at value numbering time
//
assert(arg0VN == VNNormVal(arg0VN));
if (IsVNConstant(arg0VN) && Compiler::IsTargetIntrinsic(gtMathFN))
{
- // If the math intrinsic is not implemented by target-specific instructions, such as implemented
- // by user calls, then don't do constant folding on it. This minimizes precision loss.
- // I *may* need separate tracks for the double/float -- if the intrinsic funcs have overloads for these.
- double arg0Val = GetConstantDouble(arg0VN);
+ assert(varTypeIsFloating(TypeOfVN(arg0VN)));
- double res = 0.0;
- switch (gtMathFN)
- {
- case CORINFO_INTRINSIC_Sin:
- res = sin(arg0Val);
- break;
- case CORINFO_INTRINSIC_Cos:
- res = cos(arg0Val);
- break;
- case CORINFO_INTRINSIC_Sqrt:
- res = sqrt(arg0Val);
- break;
- case CORINFO_INTRINSIC_Abs:
- res = fabs(arg0Val); // The result and params are doubles.
- break;
- case CORINFO_INTRINSIC_Round:
- res = FloatingPointUtils::round(arg0Val);
- break;
- default:
- unreached(); // the above are the only math intrinsics at the time of this writing.
- }
if (typ == TYP_DOUBLE)
{
+ // Both operand and its result must be of the same floating point type.
+ assert(typ == TypeOfVN(arg0VN));
+
+ // If the math intrinsic is not implemented by target-specific instructions, such as implemented
+ // by user calls, then don't do constant folding on it. This minimizes precision loss.
+ double arg0Val = GetConstantDouble(arg0VN);
+
+ double res = 0.0;
+ switch (gtMathFN)
+ {
+ case CORINFO_INTRINSIC_Sin:
+ res = sin(arg0Val);
+ break;
+ case CORINFO_INTRINSIC_Cos:
+ res = cos(arg0Val);
+ break;
+ case CORINFO_INTRINSIC_Sqrt:
+ res = sqrt(arg0Val);
+ break;
+ case CORINFO_INTRINSIC_Abs:
+ res = fabs(arg0Val);
+ break;
+ case CORINFO_INTRINSIC_Round:
+ res = FloatingPointUtils::round(arg0Val);
+ break;
+ default:
+ unreached(); // the above are the only math intrinsics at the time of this writing.
+ }
+
return VNForDoubleCon(res);
}
else if (typ == TYP_FLOAT)
{
- return VNForFloatCon(float(res));
+ // Both operand and its result must be of the same floating point type.
+ assert(typ == TypeOfVN(arg0VN));
+
+ // If the math intrinsic is not implemented by target-specific instructions, such as implemented
+ // by user calls, then don't do constant folding on it. This minimizes precision loss.
+ float arg0Val = GetConstantSingle(arg0VN);
+
+ float res = 0.0f;
+ switch (gtMathFN)
+ {
+ case CORINFO_INTRINSIC_Sin:
+ res = sinf(arg0Val);
+ break;
+ case CORINFO_INTRINSIC_Cos:
+ res = cosf(arg0Val);
+ break;
+ case CORINFO_INTRINSIC_Sqrt:
+ res = sqrtf(arg0Val);
+ break;
+ case CORINFO_INTRINSIC_Abs:
+ res = fabsf(arg0Val);
+ break;
+ case CORINFO_INTRINSIC_Round:
+ res = FloatingPointUtils::round(arg0Val);
+ break;
+ default:
+ unreached(); // the above are the only math intrinsics at the time of this writing.
+ }
+
+ return VNForFloatCon(res);
}
else
{
assert(typ == TYP_INT);
assert(gtMathFN == CORINFO_INTRINSIC_Round);
- return VNForIntCon(int(res));
+ int res = 0;
+ if (gtMathFN == CORINFO_INTRINSIC_Round)
+ {
+ switch (TypeOfVN(arg0VN))
+ {
+ case TYP_DOUBLE:
+ {
+ double arg0Val = GetConstantDouble(arg0VN);
+ res = int(FloatingPointUtils::round(arg0Val));
+ break;
+ }
+ case TYP_FLOAT:
+ {
+ float arg0Val = GetConstantSingle(arg0VN);
+ res = int(FloatingPointUtils::round(arg0Val));
+ break;
+ }
+ default:
+ unreached();
+ }
+ }
+ else
+ {
+ unreached(); // the above is the only math intrinsics at the time of this writing.
+ }
+
+ return VNForIntCon(res);
}
}
else