diff --git a/eng/scripts/generate_alltypes.py b/eng/scripts/generate_alltypes.py index 75514671c..fe9366c37 100644 --- a/eng/scripts/generate_alltypes.py +++ b/eng/scripts/generate_alltypes.py @@ -131,6 +131,17 @@ def is_implicit(self, oty): [SpecialName] public static %(type)s %(method_name)s(%(type)s x) => (%(type)s)(%(symbol)s(x));""" +round_method = """ +public static object __round__(%(type)s x, object ndigits) { + var result = BigIntegerOps.__round__(x, ndigits); + if (%(type)s.MinValue <= result && result <= %(type)s.MaxValue) { + return unchecked((%(type)s)result); + } + if (int.MinValue <= result && result <= int.MaxValue) { + return (int)result; + } + return result; +}""" signed_abs = """ [SpecialName] @@ -201,6 +212,10 @@ def gen_unaryops(cw, ty): cw.write(unchecked_cast_method, type=ty.name, method_name="__int__", cast_type=ty.cast_type) cw.write(unchecked_cast_method, type=ty.name, method_name="__index__", cast_type=ty.cast_type) + if ty.name not in ['Int32']: + cw.write(unchecked_cast_method, type=ty.name, method_name="__round__", cast_type=ty.cast_type) + cw.write(round_method, type=ty.name) + cw.writeline() cw.enter_block('public static int __hash__(%s x)' % (ty.name)) if ty.max > Int32.MaxValue: diff --git a/src/core/IronPython/Runtime/Operations/IntOps.Generated.cs b/src/core/IronPython/Runtime/Operations/IntOps.Generated.cs index 071e22a63..e0bb9ea9d 100644 --- a/src/core/IronPython/Runtime/Operations/IntOps.Generated.cs +++ b/src/core/IronPython/Runtime/Operations/IntOps.Generated.cs @@ -9,11 +9,11 @@ using System.Numerics; using System.Runtime.CompilerServices; +using IronPython.Runtime.Types; + using Microsoft.Scripting.Runtime; using Microsoft.Scripting.Utils; -using IronPython.Runtime.Types; - #pragma warning disable 675 namespace IronPython.Runtime.Operations { @@ -105,6 +105,19 @@ public static object Abs(SByte x) { public static int __index__(SByte x) => unchecked((int)x); + public static int __round__(SByte x) => unchecked((int)x); + + public static object __round__(SByte x, object ndigits) { + var result = BigIntegerOps.__round__(x, ndigits); + if (SByte.MinValue <= result && result <= SByte.MaxValue) { + return unchecked((SByte)result); + } + if (int.MinValue <= result && result <= int.MaxValue) { + return (int)result; + } + return result; + } + public static int __hash__(SByte x) { if (x == -1) return -2; return unchecked((int)x); @@ -378,6 +391,19 @@ public static object __new__(PythonType cls, object value) { public static int __index__(Byte x) => unchecked((int)x); + public static int __round__(Byte x) => unchecked((int)x); + + public static object __round__(Byte x, object ndigits) { + var result = BigIntegerOps.__round__(x, ndigits); + if (Byte.MinValue <= result && result <= Byte.MaxValue) { + return unchecked((Byte)result); + } + if (int.MinValue <= result && result <= int.MaxValue) { + return (int)result; + } + return result; + } + public static int __hash__(Byte x) { return unchecked((int)x); } @@ -753,6 +779,19 @@ public static object Abs(Int16 x) { public static int __index__(Int16 x) => unchecked((int)x); + public static int __round__(Int16 x) => unchecked((int)x); + + public static object __round__(Int16 x, object ndigits) { + var result = BigIntegerOps.__round__(x, ndigits); + if (Int16.MinValue <= result && result <= Int16.MaxValue) { + return unchecked((Int16)result); + } + if (int.MinValue <= result && result <= int.MaxValue) { + return (int)result; + } + return result; + } + public static int __hash__(Int16 x) { if (x == -1) return -2; return unchecked((int)x); @@ -1031,6 +1070,19 @@ public static object __new__(PythonType cls, object value) { public static int __index__(UInt16 x) => unchecked((int)x); + public static int __round__(UInt16 x) => unchecked((int)x); + + public static object __round__(UInt16 x, object ndigits) { + var result = BigIntegerOps.__round__(x, ndigits); + if (UInt16.MinValue <= result && result <= UInt16.MaxValue) { + return unchecked((UInt16)result); + } + if (int.MinValue <= result && result <= int.MaxValue) { + return (int)result; + } + return result; + } + public static int __hash__(UInt16 x) { return unchecked((int)x); } @@ -1669,6 +1721,19 @@ public static object __new__(PythonType cls, object value) { public static BigInteger __index__(UInt32 x) => unchecked((BigInteger)x); + public static BigInteger __round__(UInt32 x) => unchecked((BigInteger)x); + + public static object __round__(UInt32 x, object ndigits) { + var result = BigIntegerOps.__round__(x, ndigits); + if (UInt32.MinValue <= result && result <= UInt32.MaxValue) { + return unchecked((UInt32)result); + } + if (int.MinValue <= result && result <= int.MaxValue) { + return (int)result; + } + return result; + } + public static int __hash__(UInt32 x) { return unchecked((int)((x >= int.MaxValue) ? (x % int.MaxValue) : x)); } @@ -2054,6 +2119,19 @@ public static object Abs(Int64 x) { public static BigInteger __index__(Int64 x) => unchecked((BigInteger)x); + public static BigInteger __round__(Int64 x) => unchecked((BigInteger)x); + + public static object __round__(Int64 x, object ndigits) { + var result = BigIntegerOps.__round__(x, ndigits); + if (Int64.MinValue <= result && result <= Int64.MaxValue) { + return unchecked((Int64)result); + } + if (int.MinValue <= result && result <= int.MaxValue) { + return (int)result; + } + return result; + } + public static int __hash__(Int64 x) { if (x < 0) { if (x == long.MinValue) return -2; @@ -2330,6 +2408,19 @@ public static object __new__(PythonType cls, object value) { public static BigInteger __index__(UInt64 x) => unchecked((BigInteger)x); + public static BigInteger __round__(UInt64 x) => unchecked((BigInteger)x); + + public static object __round__(UInt64 x, object ndigits) { + var result = BigIntegerOps.__round__(x, ndigits); + if (UInt64.MinValue <= result && result <= UInt64.MaxValue) { + return unchecked((UInt64)result); + } + if (int.MinValue <= result && result <= int.MaxValue) { + return (int)result; + } + return result; + } + public static int __hash__(UInt64 x) { return unchecked((int)((x >= int.MaxValue) ? (x % int.MaxValue) : x)); } diff --git a/tests/suite/test_regressions.py b/tests/suite/test_regressions.py index 7ed496752..71393504e 100644 --- a/tests/suite/test_regressions.py +++ b/tests/suite/test_regressions.py @@ -1735,4 +1735,38 @@ def test_errors_string_format(self): import System self.assertRaises(KeyError, lambda: System.DayOfWeek["{0}"]) + @skipUnlessIronPython() + def test_ipy3_gh1977(self): + # https://github.com/IronLanguages/ironpython3/issues/1977 + import System + + res = round(System.Single(1.23)) + self.assertTrue(isinstance(res, int)) + self.assertEqual(res, 1) + + res = round(System.Single(1.23), 2) + self.assertTrue(isinstance(res, System.Single)) + self.assertEqual(res, System.Single(1.23)) + + for t in [System.SByte, System.Byte, System.Int16, System.UInt16, System.Int32, System.UInt32, System.Int64, System.UInt64]: + # round with no ndigits is expected to return an int + res = round(t.MaxValue) + self.assertTrue(isinstance(res, int)) + self.assertEqual(res, t.MaxValue) + + # round with ndigits specified is expected to return the same type as the input + res = round(t.MaxValue, 2) + self.assertTrue(isinstance(res, t)) + self.assertEqual(res, t.MaxValue) + + # on overflow, return an int + ndigits = 1 - len(str(int(t.MaxValue))) + expected = round(int(t.MaxValue), ndigits) + res = round(t.MaxValue, ndigits) + self.assertEqual(res, expected) + if expected > int(t.MaxValue): + self.assertTrue(isinstance(res, int)) + else: + self.assertTrue(isinstance(res, t)) + run_test(__name__)