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));
}
///