[mono] Fix constant folding for Math.Round (#42951)
authorEgor Bogatov <egorbo@gmail.com>
Fri, 2 Oct 2020 22:19:06 +0000 (01:19 +0300)
committerGitHub <noreply@github.com>
Fri, 2 Oct 2020 22:19:06 +0000 (01:19 +0300)
src/libraries/System.Runtime.Extensions/tests/System/Math.cs
src/mono/mono/metadata/sysmath.c
src/mono/mono/mini/intrinsics.c
src/mono/mono/utils/mono-math.h

index 61b7772..dde176d 100644 (file)
@@ -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));
+        }
     }
 }
index 5515657..b3775e5 100644 (file)
@@ -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
index 2655d10..dccb2c5 100644 (file)
@@ -3,7 +3,9 @@
  */
 
 #include <config.h>
+#include <glib.h>
 #include <mono/utils/mono-compiler.h>
+#include <mono/utils/mono-math.h>
 #include <math.h>
 
 #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);
index 8b11cbe..0f535e8 100644 (file)
@@ -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