Browse Source

#2445: Fix `1f/6f` getting printed as `355f / (678f * (float)Math.PI)`

Also fix inconsistent float comparisons due to JIT optimizations.
pull/2560/head
Daniel Grunwald 4 years ago
parent
commit
8c508d9bbb
  1. 6
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/WellKnownConstants.cs
  2. 76
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

6
ICSharpCode.Decompiler.Tests/TestCases/Pretty/WellKnownConstants.cs

@ -71,6 +71,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -71,6 +71,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public const double Double_One = 1.0;
public const float Float_Two = 2f;
public const double Double_Two = 2.0;
public const float Float_Sixth = 1f / 6f;
public const double Double_Sixth = 1.0 / 6.0;
public const float Float_Tenth = 0.1f;
public const double Double_Tenth = 0.1;
public const float Float_PI = (float)Math.PI;
public const float Float_HalfOfPI = (float)Math.PI / 2f;
@ -152,4 +156,4 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -152,4 +156,4 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public const double Double_Negated_BeforeE = -2.7182818284590446;
public const double Double_Negated_AfterE = -2.7182818284590455;
}
}
}

76
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -22,6 +22,7 @@ using System.Collections.Immutable; @@ -22,6 +22,7 @@ using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.TypeSystem;
@ -1307,19 +1308,36 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1307,19 +1308,36 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return Math.Abs(num) < den && new int[] { 2, 3, 5 }.Any(x => den % x == 0);
}
[MethodImpl(MethodImplOptions.NoInlining)]
static bool EqualDoubles(in double val1, in double val2)
{
// We use `in double` to pass the floats through memory,
// which ensures we won't get more than 64bits of precision
return val1 == val2;
}
[MethodImpl(MethodImplOptions.NoInlining)]
static bool EqualFloats(in float val1, in float val2)
{
// We use `in float` to pass the floats through memory,
// which ensures we won't get more than 32bits of precision
return val1 == val2;
}
static bool IsEqual(long num, long den, object constantValue, bool isDouble)
{
if (isDouble)
{
return (double)constantValue == num / (double)den;
return EqualDoubles((double)constantValue, num / (double)den);
}
else
{
return (float)constantValue == num / (float)den;
return EqualFloats((float)constantValue, num / (float)den);
}
}
const int MAX_DENOMINATOR = 1000;
const int MAX_DENOMINATOR_DOUBLE = 1000;
const int MAX_DENOMINATOR_FLOAT = 360;
Expression ConvertFloatingPointLiteral(IType type, object constantValue)
{
@ -1353,6 +1371,23 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1353,6 +1371,23 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
bool useFraction = (str.Length - (str.StartsWith("-", StringComparison.OrdinalIgnoreCase) ? 2 : 1) > 5);
if (useFraction && expr == null)
{
Debug.Assert(200 < MAX_DENOMINATOR_FLOAT);
// For fractions not involving PI, use a smaller MAX_DENOMINATOR
// to avoid coincidences such as (1f/MathF.PI) == (113f/355f)
(long num, long den) = isDouble
? FractionApprox((double)constantValue, MAX_DENOMINATOR_DOUBLE)
: FractionApprox((float)constantValue, 200);
if (IsValidFraction(num, den) && IsEqual(num, den, constantValue, isDouble) && Math.Abs(den) != 1)
{
var left = MakeConstant(type, num);
var right = MakeConstant(type, den);
expr = new BinaryOperatorExpression(left, BinaryOperatorType.Divide, right);
}
}
if (useFraction && expr == null && UseSpecialConstants)
{
IType mathType;
@ -1374,21 +1409,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1374,21 +1409,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
?? TryExtractExpression(mathType, type, constantValue, "E", isDouble);
}
if (useFraction && expr == null)
{
(long num, long den) = isDouble
? FractionApprox((double)constantValue, MAX_DENOMINATOR)
: FractionApprox((float)constantValue, MAX_DENOMINATOR);
if (IsValidFraction(num, den) && IsEqual(num, den, constantValue, isDouble) && Math.Abs(num) != 1 && Math.Abs(den) != 1)
{
var left = MakeConstant(type, num);
var right = MakeConstant(type, den);
return new BinaryOperatorExpression(left, BinaryOperatorType.Divide, right).WithoutILInstruction()
.WithRR(new ConstantResolveResult(type, constantValue));
}
}
if (expr == null)
expr = new PrimitiveExpression(constantValue);
@ -1458,14 +1478,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1458,14 +1478,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
double field = memberName == "PI" ? Math.PI : Math.E;
double approxValue = field * n / d;
if (approxValue == (double)literalValue)
if (EqualDoubles(approxValue, (double)literalValue))
return expr;
}
else
{
float field = memberName == "PI" ? MathF_PI : MathF_E;
float approxValue = field * n / d;
if (approxValue == (float)literalValue)
if (EqualFloats(approxValue, (float)literalValue))
return expr;
}
@ -1488,14 +1508,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1488,14 +1508,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
double field = memberName == "PI" ? Math.PI : Math.E;
double approxValue = (double)n / ((double)d * field);
if (approxValue == (double)literalValue)
if (EqualDoubles(approxValue, (double)literalValue))
return expr;
}
else
{
float field = memberName == "PI" ? MathF_PI : MathF_E;
float approxValue = (float)n / ((float)d * field);
if (approxValue == (float)literalValue)
if (EqualFloats(approxValue, (float)literalValue))
return expr;
}
@ -1503,17 +1523,19 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1503,17 +1523,19 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
(long num, long den) = isDouble
? FractionApprox((double)literalValue / (memberName == "PI" ? Math.PI : Math.E), MAX_DENOMINATOR)
: FractionApprox((float)literalValue / (memberName == "PI" ? MathF_PI : MathF_E), MAX_DENOMINATOR);
? FractionApprox((double)literalValue / (memberName == "PI" ? Math.PI : Math.E), MAX_DENOMINATOR_DOUBLE)
: FractionApprox((float)literalValue / (memberName == "PI" ? MathF_PI : MathF_E), MAX_DENOMINATOR_FLOAT);
if (IsValidFraction(num, den))
{
return ExtractExpression(num, den);
var expr = ExtractExpression(num, den);
if (expr != null)
return expr;
}
(num, den) = isDouble
? FractionApprox((double)literalValue * (memberName == "PI" ? Math.PI : Math.E), MAX_DENOMINATOR)
: FractionApprox((float)literalValue * (memberName == "PI" ? MathF_PI : MathF_E), MAX_DENOMINATOR);
? FractionApprox((double)literalValue * (memberName == "PI" ? Math.PI : Math.E), MAX_DENOMINATOR_DOUBLE)
: FractionApprox((float)literalValue * (memberName == "PI" ? MathF_PI : MathF_E), MAX_DENOMINATOR_FLOAT);
if (IsValidFraction(num, den))
{

Loading…
Cancel
Save