From 21f515290ea1a139940b3de4f4facd12867ff931 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sat, 3 Oct 2020 01:19:06 +0300 Subject: [PATCH] [mono] Fix constant folding for Math.Round (#42951) --- .../System.Runtime.Extensions/tests/System/Math.cs | 98 ++++++++++++++++++++++ src/mono/mono/metadata/sysmath.c | 16 +--- src/mono/mono/mini/intrinsics.c | 4 +- src/mono/mono/utils/mono-math.h | 19 +++++ 4 files changed, 122 insertions(+), 15 deletions(-) diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Math.cs b/src/libraries/System.Runtime.Extensions/tests/System/Math.cs index 61b7772..dde176d 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Math.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Math.cs @@ -2615,5 +2615,103 @@ namespace System.Tests Assert.Equal(expected, Math.Round(x, 0, mode)); Assert.Equal(expected, decimal.Round(x, 0, mode)); } + + [Fact] + public static void Round_Double_Constant_Arg() + { + Assert.Equal( 0, Math.Round( 0.5)); + Assert.Equal( 0, Math.Round(-0.5)); + Assert.Equal( 1, Math.Round( 1.0)); + Assert.Equal(-1, Math.Round(-1.0)); + Assert.Equal( 2, Math.Round( 1.5)); + Assert.Equal(-2, Math.Round(-1.5)); + Assert.Equal( 2, Math.Round( 2.0)); + Assert.Equal(-2, Math.Round(-2.0)); + Assert.Equal( 2, Math.Round( 2.5)); + Assert.Equal(-2, Math.Round(-2.5)); + Assert.Equal( 3, Math.Round( 3.0)); + Assert.Equal(-3, Math.Round(-3.0)); + Assert.Equal( 4, Math.Round( 3.5)); + Assert.Equal(-4, Math.Round(-3.5)); + + Assert.Equal( 0, Math.Round( 0.5, MidpointRounding.ToZero)); + Assert.Equal( 0, Math.Round( 0.5, MidpointRounding.ToZero)); + Assert.Equal( 1, Math.Round( 1.0, MidpointRounding.ToZero)); + Assert.Equal(-1, Math.Round(-1.0, MidpointRounding.ToZero)); + Assert.Equal( 1, Math.Round( 1.5, MidpointRounding.ToZero)); + Assert.Equal(-1, Math.Round(-1.5, MidpointRounding.ToZero)); + Assert.Equal( 2, Math.Round( 2.0, MidpointRounding.ToZero)); + Assert.Equal(-2, Math.Round(-2.0, MidpointRounding.ToZero)); + Assert.Equal( 2, Math.Round( 2.5, MidpointRounding.ToZero)); + Assert.Equal(-2, Math.Round(-2.5, MidpointRounding.ToZero)); + Assert.Equal( 3, Math.Round( 3.0, MidpointRounding.ToZero)); + Assert.Equal(-3, Math.Round(-3.0, MidpointRounding.ToZero)); + Assert.Equal( 3, Math.Round( 3.5, MidpointRounding.ToZero)); + Assert.Equal(-3, Math.Round(-3.5, MidpointRounding.ToZero)); + + Assert.Equal( 1, Math.Round( 0.5, MidpointRounding.AwayFromZero)); + Assert.Equal( 1, Math.Round( 0.5, MidpointRounding.AwayFromZero)); + Assert.Equal( 1, Math.Round( 1.0, MidpointRounding.AwayFromZero)); + Assert.Equal(-1, Math.Round(-1.0, MidpointRounding.AwayFromZero)); + Assert.Equal( 2, Math.Round( 1.5, MidpointRounding.AwayFromZero)); + Assert.Equal(-2, Math.Round(-1.5, MidpointRounding.AwayFromZero)); + Assert.Equal( 2, Math.Round( 2.0, MidpointRounding.AwayFromZero)); + Assert.Equal(-2, Math.Round(-2.0, MidpointRounding.AwayFromZero)); + Assert.Equal( 3, Math.Round( 2.5, MidpointRounding.AwayFromZero)); + Assert.Equal(-3, Math.Round(-2.5, MidpointRounding.AwayFromZero)); + Assert.Equal( 3, Math.Round( 3.0, MidpointRounding.AwayFromZero)); + Assert.Equal(-3, Math.Round(-3.0, MidpointRounding.AwayFromZero)); + Assert.Equal( 4, Math.Round( 3.5, MidpointRounding.AwayFromZero)); + Assert.Equal(-4, Math.Round(-3.5, MidpointRounding.AwayFromZero)); + } + + [Fact] + public static void Round_Float_Constant_Arg() + { + Assert.Equal( 0, MathF.Round( 0.5f)); + Assert.Equal( 0, MathF.Round(-0.5f)); + Assert.Equal( 1, MathF.Round( 1.0f)); + Assert.Equal(-1, MathF.Round(-1.0f)); + Assert.Equal( 2, MathF.Round( 1.5f)); + Assert.Equal(-2, MathF.Round(-1.5f)); + Assert.Equal( 2, MathF.Round( 2.0f)); + Assert.Equal(-2, MathF.Round(-2.0f)); + Assert.Equal( 2, MathF.Round( 2.5f)); + Assert.Equal(-2, MathF.Round(-2.5f)); + Assert.Equal( 3, MathF.Round( 3.0f)); + Assert.Equal(-3, MathF.Round(-3.0f)); + Assert.Equal( 4, MathF.Round( 3.5f)); + Assert.Equal(-4, MathF.Round(-3.5f)); + + Assert.Equal( 0, MathF.Round( 0.5f, MidpointRounding.ToZero)); + Assert.Equal( 0, MathF.Round( 0.5f, MidpointRounding.ToZero)); + Assert.Equal( 1, MathF.Round( 1.0f, MidpointRounding.ToZero)); + Assert.Equal(-1, MathF.Round(-1.0f, MidpointRounding.ToZero)); + Assert.Equal( 1, MathF.Round( 1.5f, MidpointRounding.ToZero)); + Assert.Equal(-1, MathF.Round(-1.5f, MidpointRounding.ToZero)); + Assert.Equal( 2, MathF.Round( 2.0f, MidpointRounding.ToZero)); + Assert.Equal(-2, MathF.Round(-2.0f, MidpointRounding.ToZero)); + Assert.Equal( 2, MathF.Round( 2.5f, MidpointRounding.ToZero)); + Assert.Equal(-2, MathF.Round(-2.5f, MidpointRounding.ToZero)); + Assert.Equal( 3, MathF.Round( 3.0f, MidpointRounding.ToZero)); + Assert.Equal(-3, MathF.Round(-3.0f, MidpointRounding.ToZero)); + Assert.Equal( 3, MathF.Round( 3.5f, MidpointRounding.ToZero)); + Assert.Equal(-3, MathF.Round(-3.5f, MidpointRounding.ToZero)); + + Assert.Equal( 1, MathF.Round( 0.5f, MidpointRounding.AwayFromZero)); + Assert.Equal( 1, MathF.Round( 0.5f, MidpointRounding.AwayFromZero)); + Assert.Equal( 1, MathF.Round( 1.0f, MidpointRounding.AwayFromZero)); + Assert.Equal(-1, MathF.Round(-1.0f, MidpointRounding.AwayFromZero)); + Assert.Equal( 2, MathF.Round( 1.5f, MidpointRounding.AwayFromZero)); + Assert.Equal(-2, MathF.Round(-1.5f, MidpointRounding.AwayFromZero)); + Assert.Equal( 2, MathF.Round( 2.0f, MidpointRounding.AwayFromZero)); + Assert.Equal(-2, MathF.Round(-2.0f, MidpointRounding.AwayFromZero)); + Assert.Equal( 3, MathF.Round( 2.5f, MidpointRounding.AwayFromZero)); + Assert.Equal(-3, MathF.Round(-2.5f, MidpointRounding.AwayFromZero)); + Assert.Equal( 3, MathF.Round( 3.0f, MidpointRounding.AwayFromZero)); + Assert.Equal(-3, MathF.Round(-3.0f, MidpointRounding.AwayFromZero)); + Assert.Equal( 4, MathF.Round( 3.5f, MidpointRounding.AwayFromZero)); + Assert.Equal(-4, MathF.Round(-3.5f, MidpointRounding.AwayFromZero)); + } } } diff --git a/src/mono/mono/metadata/sysmath.c b/src/mono/mono/metadata/sysmath.c index 5515657..b3775e5 100644 --- a/src/mono/mono/metadata/sysmath.c +++ b/src/mono/mono/metadata/sysmath.c @@ -28,6 +28,7 @@ #include "number-ms.h" #include "utils/mono-compiler.h" +#include "utils/mono-math.h" #include "icalls.h" #include "icall-decl.h" @@ -40,20 +41,7 @@ ves_icall_System_Math_Floor (gdouble x) gdouble ves_icall_System_Math_Round (gdouble x) { - gdouble floor_tmp; - - /* 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 == (gdouble)(gint64) x) - return x; - - floor_tmp = floor (x + 0.5); - - if ((x == (floor (x) + 0.5)) && (fmod (floor_tmp, 2.0) != 0)) { - floor_tmp -= 1.0; - } - - return copysign (floor_tmp, x); + return mono_round_to_even (x); } gdouble diff --git a/src/mono/mono/mini/intrinsics.c b/src/mono/mono/mini/intrinsics.c index 2655d10..dccb2c5 100644 --- a/src/mono/mono/mini/intrinsics.c +++ b/src/mono/mono/mini/intrinsics.c @@ -3,7 +3,9 @@ */ #include +#include #include +#include #include #ifndef DISABLE_JIT @@ -1763,7 +1765,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign result = cosh (source); break; case OP_ROUND: - result = round (source); + result = mono_round_to_even (source); break; case OP_SIN: result = sin (source); diff --git a/src/mono/mono/utils/mono-math.h b/src/mono/mono/utils/mono-math.h index 8b11cbe..0f535e8 100644 --- a/src/mono/mono/utils/mono-math.h +++ b/src/mono/mono/utils/mono-math.h @@ -80,4 +80,23 @@ inline double mono_trunc (double a) { return mono_trunc_double ( #endif +static inline double +mono_round_to_even (double x) +{ + double floor_tmp; + + /* 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_t) x) + return x; + + floor_tmp = floor (x + 0.5); + + if ((x == (floor (x) + 0.5)) && (fmod (floor_tmp, 2.0) != 0)) { + floor_tmp -= 1.0; + } + + return copysign (floor_tmp, x); +} + #endif -- 2.7.4