diff --git a/src/libraries/System.Private.CoreLib/src/System/Int128.cs b/src/libraries/System.Private.CoreLib/src/System/Int128.cs index 50dce177e17135..7a6262dcf5be07 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int128.cs @@ -1290,22 +1290,33 @@ public static Int128 Clamp(Int128 value, Int128 min, Int128 max) /// public static Int128 CopySign(Int128 value, Int128 sign) { - Int128 absValue = value; - - if (IsNegative(absValue)) - { - absValue = -absValue; - } - - if (IsPositive(sign)) +#if TARGET_32BIT + Int128 t = value; + if (Int128.IsPositive(sign)) { - if (IsNegative(absValue)) + if (Int128.IsNegative(t)) { - Math.ThrowNegateTwosCompOverflow(); + t = -t; + if (Int128.IsNegative(t)) + { + Math.ThrowNegateTwosCompOverflow(); + } } - return absValue; + return t; } - return -absValue; + if (Int128.IsPositive(t)) + { + t = -t; + } + return t; +#else + // Int128.MinValue == value && 0 <= sign + if ((long)value._upper == long.MinValue && value._lower == 0 && (long)sign._upper >= 0) + { + Math.ThrowNegateTwosCompOverflow(); + } + return value * Math.SignOrOneIfZero((long)value._upper ^ (long)sign._upper); +#endif } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Int16.cs b/src/libraries/System.Private.CoreLib/src/System/Int16.cs index af144fb860c921..24ee8c4a92928b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int16.cs @@ -631,24 +631,11 @@ public static short Log2(short value) /// public static short CopySign(short value, short sign) { - short absValue = value; - - if (absValue < 0) - { - absValue = (short)(-absValue); - } - - if (sign >= 0) + if (value == short.MinValue && sign >= 0) { - if (absValue < 0) - { - Math.ThrowNegateTwosCompOverflow(); - } - - return absValue; + Math.ThrowNegateTwosCompOverflow(); } - - return (short)(-absValue); + return (short)(value * Math.SignOrOneIfZero(value ^ sign)); } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index 2f91c2c2a59702..717f4c3762f962 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -666,24 +666,11 @@ public static int Log2(int value) /// public static int CopySign(int value, int sign) { - int absValue = value; - - if (absValue < 0) - { - absValue = -absValue; - } - - if (sign >= 0) + if (value == int.MinValue && sign >= 0) { - if (absValue < 0) - { - Math.ThrowNegateTwosCompOverflow(); - } - - return absValue; + Math.ThrowNegateTwosCompOverflow(); } - - return -absValue; + return value * Math.SignOrOneIfZero(value ^ sign); } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index ca170d8c903ed0..254d8a5c38add7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -661,26 +661,14 @@ public static long Log2(long value) public static long Clamp(long value, long min, long max) => Math.Clamp(value, min, max); /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long CopySign(long value, long sign) { - long absValue = value; - - if (absValue < 0) + if (value == long.MinValue && sign >= 0) { - absValue = -absValue; + Math.ThrowNegateTwosCompOverflow(); } - - if (sign >= 0) - { - if (absValue < 0) - { - Math.ThrowNegateTwosCompOverflow(); - } - - return absValue; - } - - return -absValue; + return value * Math.SignOrOneIfZero(value ^ sign); } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs index 3dd33c2c66b82d..0a4c87e5bd8990 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs @@ -674,24 +674,11 @@ public static nint Log2(nint value) /// public static nint CopySign(nint value, nint sign) { - nint absValue = value; - - if (absValue < 0) - { - absValue = -absValue; - } - - if (sign >= 0) + if (value == nint.MinValue && sign >= 0) { - if (absValue < 0) - { - Math.ThrowNegateTwosCompOverflow(); - } - - return absValue; + Math.ThrowNegateTwosCompOverflow(); } - - return -absValue; + return value * Math.SignOrOneIfZero(value ^ sign); } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Math.cs b/src/libraries/System.Private.CoreLib/src/System/Math.cs index 7d20cc72202dd1..841ecdc2305bd9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Math.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Math.cs @@ -1468,6 +1468,27 @@ public static int Sign(float value) throw new ArithmeticException(SR.Arithmetic_NaN); } + // Helper functions for CopySign + // >= 0: returns 1 + // < 0: returns -1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int SignOrOneIfZero(int value) + { + return (value >> (32 - 2)) | 1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int SignOrOneIfZero(nint value) + { + return (int)(value >> (8 * nint.Size - 2)) | 1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int SignOrOneIfZero(long value) + { + return (int)(value >> (64 - 2)) | 1; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static decimal Truncate(decimal d) { diff --git a/src/libraries/System.Private.CoreLib/src/System/SByte.cs b/src/libraries/System.Private.CoreLib/src/System/SByte.cs index fb4734d41b69f6..0d61ff474edd23 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SByte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SByte.cs @@ -592,24 +592,11 @@ public static sbyte Log2(sbyte value) /// public static sbyte CopySign(sbyte value, sbyte sign) { - sbyte absValue = value; - - if (absValue < 0) - { - absValue = (sbyte)(-absValue); - } - - if (sign >= 0) + if (value == sbyte.MinValue && sign >= 0) { - if (absValue < 0) - { - Math.ThrowNegateTwosCompOverflow(); - } - - return absValue; + Math.ThrowNegateTwosCompOverflow(); } - - return (sbyte)(-absValue); + return (sbyte)(value * Math.SignOrOneIfZero(value ^ sign)); } ///