From 9a04e81e52fc396e4945f2c14229f4b40f8b41bb Mon Sep 17 00:00:00 2001 From: Drew Kersnar <18474647+dakersnar@users.noreply.github.com> Date: Thu, 10 Nov 2022 10:52:53 -0600 Subject: [PATCH] Fix bug with BigInteger.TrailingZeroCount (#77727) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix bug with BigInteger.TrailingZeroCount * Update src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs Co-authored-by: Tommi Kekäläinen <76913562+tkekalainen@users.noreply.github.com> * Update negative code path * Simplify logic for handling negative values * Update src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs Co-authored-by: Tommi Kekäläinen <76913562+tkekalainen@users.noreply.github.com> * Update comment Co-authored-by: Tommi Kekäläinen <76913562+tkekalainen@users.noreply.github.com> --- .../src/System/Numerics/BigInteger.cs | 39 ++++--------------- .../tests/BigIntegerTests.GenericMath.cs | 3 ++ 2 files changed, 11 insertions(+), 31 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 1175591a97e005..f55acb76559d63 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -3481,42 +3481,19 @@ public static BigInteger TrailingZeroCount(BigInteger value) ulong result = 0; - if (value._sign >= 0) - { - // When the value is positive, we simply need to do a tzcnt for all bits until we find one set - - uint part = value._bits[0]; + // Both positive values and their two's-complement negative representation will share the same TrailingZeroCount, + // so the sign of value does not matter and both cases can be handled in the same way - for (int i = 1; (part == 0) && (i < value._bits.Length); i++) - { - part = value._bits[i]; - result += (sizeof(uint) * 8); + uint part = value._bits[0]; - i++; - } - - result += uint.TrailingZeroCount(part); - } - else + for (int i = 1; (part == 0) && (i < value._bits.Length); i++) { - // When the value is negative, we need to tzcnt the two's complement representation - // We'll do this "inline" to avoid needing to unnecessarily allocate. - - uint part = ~value._bits[0] + 1; - - for (int i = 1; (part == 0) && (i < value._bits.Length); i++) - { - // Simply process bits, adding the carry while the previous value is zero - - part = ~value._bits[i] + 1; - result += (sizeof(uint) * 8); - - i++; - } - - result += uint.TrailingZeroCount(part); + part = value._bits[i]; + result += (sizeof(uint) * 8); } + result += uint.TrailingZeroCount(part); + return result; } diff --git a/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs b/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs index d4e06061325aa0..3e19f1cf22e97e 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigIntegerTests.GenericMath.cs @@ -366,6 +366,9 @@ public static void TrailingZeroCountTest() Assert.Equal((BigInteger)63, BinaryIntegerHelper.TrailingZeroCount(Int64MaxValuePlusOne)); Assert.Equal((BigInteger)0, BinaryIntegerHelper.TrailingZeroCount(UInt64MaxValue)); + + Assert.Equal((BigInteger)1000, BinaryIntegerHelper.TrailingZeroCount(BigInteger.Pow(2, 1000))); + Assert.Equal((BigInteger)1000, BinaryIntegerHelper.TrailingZeroCount(-BigInteger.Pow(2, 1000))); } [Fact]