diff --git a/DivideSharp.Tests/Int32DivisionTests.cs b/DivideSharp.Tests/Int32DivisionTests.cs index 69e8c1e..23144e9 100644 --- a/DivideSharp.Tests/Int32DivisionTests.cs +++ b/DivideSharp.Tests/Int32DivisionTests.cs @@ -50,15 +50,9 @@ private static IEnumerable DivisionTestCaseSource yield return new TestCaseData(item, item - 1); yield return new TestCaseData(item, item); yield return new TestCaseData(item, item + 1); - yield return new TestCaseData(item, 2 * item - 1); - yield return new TestCaseData(item, 2 * item); - yield return new TestCaseData(item, 2 * item + 1); yield return new TestCaseData(item, -item - 1); yield return new TestCaseData(item, -item); yield return new TestCaseData(item, -item + 1); - yield return new TestCaseData(item, 2 * -item - 1); - yield return new TestCaseData(item, 2 * -item); - yield return new TestCaseData(item, 2 * -item + 1); } //Branch yield return new TestCaseData(int.MinValue, 1); diff --git a/DivideSharp.Tests/Int64DivisionTests.cs b/DivideSharp.Tests/Int64DivisionTests.cs new file mode 100644 index 0000000..97a56a6 --- /dev/null +++ b/DivideSharp.Tests/Int64DivisionTests.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Newtonsoft.Json; + +using NUnit.Framework; + +using TestAndBenchmarkUtils; + +namespace DivideSharp.Tests +{ + [TestFixture] + public class Int64DivisionTests + { + #region Test Cases + + private static IEnumerable Divisors + { + get + { + var divisors = new long[] + { + //Obvious + 1, + //Pre-Defined + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + //Shift + 2, + 16, + 15, //MultiplyAdd or MultiplySub + 29 //Multiply + }; + return divisors.Concat(divisors.Select(a => -a)).Distinct().OrderBy(a => Math.Abs(a)); + } + } + + private static IEnumerable RandomDivisionTestCaseSource => Divisors.Append(long.MinValue).Select(a => new TestCaseData(a)); + + private const ulong RandomTestCount = 0x1_0000ul; + + private static IEnumerable DivisionTestCaseSource + { + get + { + var divisors = Divisors; + //Generate Test Data's + foreach (var item in divisors) + { + yield return new TestCaseData(item, 1); + yield return new TestCaseData(item, item - 1); + yield return new TestCaseData(item, item); + yield return new TestCaseData(item, item + 1); + yield return new TestCaseData(item, -item - 1); + yield return new TestCaseData(item, -item); + yield return new TestCaseData(item, -item + 1); + } + //Branch + yield return new TestCaseData(long.MinValue, 1); + yield return new TestCaseData(long.MinValue, long.MinValue); + yield return new TestCaseData(long.MinValue, -long.MaxValue); + yield return new TestCaseData(long.MinValue, 0); + yield return new TestCaseData(long.MinValue, long.MaxValue); + } + } + + #endregion Test Cases + + private static string SerializeDivisor(Int64Divisor divisor) => $"{JsonConvert.SerializeObject(divisor)}"; + + [TestCaseSource(nameof(DivisionTestCaseSource))] + public void CorrectlyDivides(long divisor, long testDividend) + { + var int64Divisor = new Int64Divisor(divisor); + var quotient = testDividend / int64Divisor; + Console.WriteLine($"quotient:{quotient}"); + Assert.AreEqual(testDividend / divisor, quotient, SerializeDivisor(int64Divisor)); + } + + [TestCaseSource(nameof(DivisionTestCaseSource))] + public void CalculatesModulusCorrectly(long divisor, long testDividend) + { + var int64Divisor = new Int64Divisor(divisor); + var remainder = testDividend % int64Divisor; + Console.WriteLine($"remainder:{remainder}"); + Assert.AreEqual(testDividend % divisor, remainder, SerializeDivisor(int64Divisor)); + } + + [TestCaseSource(nameof(DivisionTestCaseSource))] + public void DivRemReturnsCorrectly(long divisor, long testDividend) + { + var int64Divisor = new Int64Divisor(divisor); + var remainder = int64Divisor.DivRem(testDividend, out var quotient); + Console.WriteLine($"quotient:{quotient}, remainder:{remainder}"); + Assert.Multiple(() => + { + Assert.AreEqual(testDividend % divisor, remainder, SerializeDivisor(int64Divisor)); + Assert.AreEqual(testDividend / divisor, quotient, SerializeDivisor(int64Divisor)); + }); + } + + [TestCaseSource(nameof(DivisionTestCaseSource))] + public void CalculatesAbsFloorCorrectly(long divisor, long testDividend) + { + var int64Divisor = new Int64Divisor(divisor); + var rounded = int64Divisor.AbsFloor(testDividend); + Console.WriteLine($"rounded:{rounded}"); + Assert.AreEqual(testDividend / divisor * divisor, rounded, SerializeDivisor(int64Divisor)); + } + + [TestCaseSource(nameof(DivisionTestCaseSource))] + public void CalculatesAbsFloorRemCorrectly(long divisor, long testDividend) + { + var int64Divisor = new Int64Divisor(divisor); + var remainder = int64Divisor.AbsFloorRem(testDividend, out var rounded); + Console.WriteLine($"rounded:{rounded}, remainder:{remainder}"); + Assert.Multiple(() => + { + Assert.AreEqual(testDividend % divisor, remainder, SerializeDivisor(int64Divisor)); + Assert.AreEqual(testDividend / divisor * divisor, rounded, SerializeDivisor(int64Divisor)); + }); + } + + [TestCaseSource(nameof(RandomDivisionTestCaseSource))] + public void CorrectlyDividesRandomNumerators(long divisor) + { + var int64Divisor = new Int64Divisor(divisor); + var rng = new PcgRandom(); + for (ulong i = 0; i < RandomTestCount; i++) + { + var testDividend = unchecked(rng.Next() | ((long)rng.Next() << 32)); + var quotient = testDividend / int64Divisor; + Assert.AreEqual(testDividend / divisor, quotient, $"Trying to test {testDividend} / {divisor}"); + } + } + + [TestCaseSource(nameof(RandomDivisionTestCaseSource))] + public void CalculatesModulusCorrectlyRandomNumerators(long divisor) + { + var int64Divisor = new Int64Divisor(divisor); + var rng = new PcgRandom(); + + for (ulong i = 0; i < RandomTestCount; i++) + { + var testDividend = unchecked(rng.Next() | ((long)rng.Next() << 32)); + var remainder = testDividend % int64Divisor; + Assert.AreEqual(testDividend % divisor, remainder, $"Trying to test {testDividend} % {divisor}"); + } + } + + [TestCaseSource(nameof(RandomDivisionTestCaseSource))] + public void DivRemReturnsCorrectlyRandomNumerators(long divisor) + { + var int64Divisor = new Int64Divisor(divisor); + var rng = new PcgRandom(); + for (ulong i = 0; i < RandomTestCount; i++) + { + var testDividend = unchecked(rng.Next() | ((long)rng.Next() << 32)); + var remainder = int64Divisor.DivRem(testDividend, out var quotient); + Assert.AreEqual(testDividend % divisor, remainder, $"Trying to test {testDividend} % {divisor}"); + Assert.AreEqual(testDividend / divisor, quotient, $"Trying to test {testDividend} / {divisor}"); + } + } + + [TestCaseSource(nameof(RandomDivisionTestCaseSource))] + public void CalculatesAbsFloorCorrectlyRandomNumerators(long divisor) + { + var int64Divisor = new Int64Divisor(divisor); + var rng = new PcgRandom(); + for (ulong i = 0; i < RandomTestCount; i++) + { + var testDividend = unchecked(rng.Next() | ((long)rng.Next() << 32)); + var rounded = int64Divisor.AbsFloor(testDividend); + Assert.AreEqual(testDividend / divisor * divisor, rounded, $"Trying to test {testDividend} / {divisor} * {divisor}"); + } + } + + [TestCaseSource(nameof(RandomDivisionTestCaseSource))] + public void CalculatesAbsFloorRemCorrectlyRandomNumerators(long divisor) + { + var int64Divisor = new Int64Divisor(divisor); + var rng = new PcgRandom(); + for (ulong i = 0; i < RandomTestCount; i++) + { + var testDividend = unchecked(rng.Next() | ((long)rng.Next() << 32)); + var remainder = int64Divisor.AbsFloorRem(testDividend, out var rounded); + Assert.AreEqual(testDividend % divisor, remainder, $"Trying to test {testDividend} % {divisor}"); + Assert.AreEqual(testDividend / divisor * divisor, rounded, $"Trying to test {testDividend} / {divisor} * {divisor}"); + } + } + } +} diff --git a/DivideSharp.Tests/MiscTests.cs b/DivideSharp.Tests/MiscTests.cs index f61fe10..b9c434f 100644 --- a/DivideSharp.Tests/MiscTests.cs +++ b/DivideSharp.Tests/MiscTests.cs @@ -38,7 +38,7 @@ public static uint MultiplyHigh32(uint a, uint b) } [Test] - public void MulHiEquals() + public void MulHiEqualsUnsigned() { var rng = new PcgRandom(); for (ulong i = 0; i < RandomTestCount; i++) @@ -48,5 +48,50 @@ public void MulHiEquals() Assert.AreEqual(MultiplyHigh32(a, b), MultiplyHighCustom32(a, b), $"Testing {a} * {b}"); } } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int MultiplyHighCustom32(int x, int y) + { + unchecked + { + uint xl = (ushort)x; + uint yl = (ushort)y; + int xh = x >> 16; + int yh = y >> 16; + uint u = xl * yl; + int s = (int)(u >> 16); + int t1 = xh * (int)yl; + int t2 = yh * (int)xl; + s += (ushort)t1; + s += (ushort)t2; + s >>= 16; + s += (t1 >> 16) + (t2 >> 16); + s += xh * yh; + return s; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int MultiplyHigh32(int a, int b) + { + long h = a; + h *= b; + return (int)(h >> 32); + } + + [Test] + public void MulHiEqualsSigned() + { + unchecked + { + var rng = new PcgRandom(); + for (ulong i = 0; i < RandomTestCount; i++) + { + var a = (int)rng.Next(); + var b = (int)rng.Next(); + Assert.AreEqual(MultiplyHigh32(a, b), MultiplyHighCustom32(a, b), $"Testing {a} * {b}"); + } + } + } } } diff --git a/DivideSharp/DivideSharp.xml b/DivideSharp/DivideSharp.xml index 03fa00a..b82bce4 100644 --- a/DivideSharp/DivideSharp.xml +++ b/DivideSharp/DivideSharp.xml @@ -34,42 +34,42 @@ The strategy for >MaxValue/2 that only branches. - + Represents a strategy of division for . - + The strategy for dividing by 1. - + The strategy for dividing by the number that is power of two. - + The strategy for dividing by the negated number that is power of two. - + The strategy multiplies and shifts. - + The strategy multiplies, adds, and shifts. - + The strategy multiplies, subtracts, and shifts. - + The strategy for MinValue that only branches. @@ -149,6 +149,167 @@ A multiple of that has the largest absolute value less than the absolute value of the specified . The difference between and . + + + Divides an value RAPIDLY. + + + + + + Gets the divisor. + + + The divisor. + + + + + Gets the multiplier for actual "division". + + + The multiplier. + + + + + Gets the strategy of a division. + + + The strategy of a division. + + + + + Gets the number of bits to shift for actual "division". + + + The number of bits to shift right. + + + + + Gets the mask to divide. + + + The mask. + + + + + Initializes a new instance of the struct. + + The divisor. + The multiplier. + The strategy. + The shift. + + + + Initializes a new instance of the struct. + + The divisor. + + + + + Divides the specified by . + + The dividend. + The value divided by . + + + + Calculates the quotient of two 32-bit signed integers ( and ) and the remainder. + + The dividend. + The quotient of the specified numbers. + The remainder. + + + + Returns a multiple of that has the largest absolute value less than the absolute value of the specified . + + The dividend. + A multiple of that has the largest absolute value less than the absolute value of the specified . + + + + Calculates a multiple of that has the largest absolute value less than the absolute value of the specified and stores it in , and Returns the difference between and . + + The value. + A multiple of that has the largest absolute value less than the absolute value of the specified . + The difference between and . + + + + Calculates the remainder by of the specified . + + The dividend. + The remainder. + + + + Indicates whether the current object is equal to another object of the same type. + + An object to compare with this object. + + true if the current object is equal to the obj parameter; otherwise, false. + + + + + Indicates whether the current object is equal to another object of the same type. + + An object to compare with this object. + + true if the current object is equal to the other parameter; otherwise, false. + + + + + Returns a hash code for this instance. + + + A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + + + + + Rapidly divides the specified value with 's . + + The dividend. + The divisor. + The result of dividing by . + + + + Rapidly returns the remainder resulting from dividing the specified value with 's . + + The dividend. + The divisor. + The remainder resulting from dividing by . + + + + Indicates whether the values of two specified objects are equal. + + The first to compare. + The second to compare. + + true if the left is the same as the right; otherwise, false. + + + + + Indicates whether the values of two specified objects are not equal. + + The first to compare. + The second to compare. + + true if left and right are not equal; otherwise, false. + + Divides an value RAPIDLY. @@ -195,7 +356,7 @@ The mask. - + Initializes a new instance of the struct. @@ -310,6 +471,320 @@ true if left and right are not equal; otherwise, false. + + + Divides an value RAPIDLY. + + + + + + Gets the divisor. + + + The divisor. + + + + + Gets the multiplier for actual "division". + + + The multiplier. + + + + + Gets the strategy of a division. + + + The strategy of a division. + + + + + Gets the number of bits to shift for actual "division". + + + The number of bits to shift right. + + + + + Gets the mask to divide. + + + The mask. + + + + + Initializes a new instance of the struct. + + The divisor. + The multiplier. + The strategy. + The shift. + + + + Initializes a new instance of the struct. + + The divisor. + + + + + Divides the specified by . + + The dividend. + The value divided by . + + + + Calculates the quotient of two 32-bit signed integers ( and ) and the remainder. + + The dividend. + The quotient of the specified numbers. + The remainder. + + + + Returns a multiple of that has the largest absolute value less than the absolute value of the specified . + + The dividend. + A multiple of that has the largest absolute value less than the absolute value of the specified . + + + + Calculates a multiple of that has the largest absolute value less than the absolute value of the specified and stores it in , and Returns the difference between and . + + The value. + A multiple of that has the largest absolute value less than the absolute value of the specified . + The difference between and . + + + + Calculates the remainder by of the specified . + + The dividend. + The remainder. + + + + Indicates whether the current object is equal to another object of the same type. + + An object to compare with this object. + + true if the current object is equal to the obj parameter; otherwise, false. + + + + + Indicates whether the current object is equal to another object of the same type. + + An object to compare with this object. + + true if the current object is equal to the other parameter; otherwise, false. + + + + + Returns a hash code for this instance. + + + A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + + + + + Rapidly divides the specified value with 's . + + The dividend. + The divisor. + The result of dividing by . + + + + Rapidly returns the remainder resulting from dividing the specified value with 's . + + The dividend. + The divisor. + The remainder resulting from dividing by . + + + + Indicates whether the values of two specified objects are equal. + + The first to compare. + The second to compare. + + true if the left is the same as the right; otherwise, false. + + + + + Indicates whether the values of two specified objects are not equal. + + The first to compare. + The second to compare. + + true if left and right are not equal; otherwise, false. + + + + + Divides an value RAPIDLY. + + + + + + Gets the divisor. + + + The divisor. + + + + + Gets the multiplier for actual "division". + + + The multiplier. + + + + + Gets the strategy of a division. + + + The strategy of a division. + + + + + Gets the number of bits to shift for actual "division". + + + The number of bits to shift right. + + + + + Initializes a new instance of the struct. + + The divisor. + + + + + Initializes a new instance of the struct. + + The divisor. + The multiplier. + The strategy. + The shift. + + + + Divides the specified by . + + The dividend. + The value divided by . + + + + Calculates the quotient of two 32-bit unsigned integers ( and ) and the remainder. + + The dividend. + The quotient of the specified numbers. + The remainder. + + + + Returns the largest multiple of less than or equal to the specified . + + The dividend. + The largest multiple of less than or equal to the specified . + + + + Calculates the remainder by of the specified . + + The dividend. + The remainder. + + + + Calculates the largest multiple of less than or equal to the specified and the remainder. + + The value. + The largest multiple of less than or equal to the specified . + The remainder. + + + + Indicates whether this instance and a specified object are equal. + + The object to compare with the current instance. + + true if obj and this instance are the same type and represent the same value; otherwise, false. + + + + + Indicates whether the current object is equal to another object of the same type. + + An object to compare with this object. + + true if the current object is equal to the other parameter; otherwise, false. + + + + + Returns the hash code for this instance. + + + A 32-bit signed integer that is the hash code for this instance. + + + + + Rapidly divides the specified value with 's . + + The dividend. + The divisor. + The result of dividing by . + + + + Rapidly returns the remainder resulting from dividing the specified value with 's . + + The dividend. + The divisor. + The remainder resulting from dividing by . + + + + Indicates whether the values of two specified objects are equal. + + The first to compare. + The second to compare. + + true if the left is the same as the right; otherwise, false. + + + + + Indicates whether the values of two specified objects are not equal. + + The first to compare. + The second to compare. + + true if left and right are not equal; otherwise, false. + + Divides an value RAPIDLY. @@ -683,6 +1158,13 @@ The y. + + + Returns the absolute value of the specified . + + The value. + + Returns the absolute value of the specified . @@ -690,5 +1172,12 @@ The value. + + + Returns the absolute value of the specified . + + The value. + + diff --git a/DivideSharp/DivisorStrategy.cs b/DivideSharp/DivisorStrategy.cs index 0f33be5..e497778 100644 --- a/DivideSharp/DivisorStrategy.cs +++ b/DivideSharp/DivisorStrategy.cs @@ -38,7 +38,7 @@ public enum UnsignedIntegerDivisorStrategy : byte /// /// Represents a strategy of division for . /// - public enum Int32DivisorStrategy : byte + public enum SignedDivisorStrategy : byte { /// /// The strategy for dividing by 1. diff --git a/DivideSharp/Int32Divisor.cs b/DivideSharp/Int32Divisor.cs index 3d6654d..ca2d027 100644 --- a/DivideSharp/Int32Divisor.cs +++ b/DivideSharp/Int32Divisor.cs @@ -15,33 +15,33 @@ namespace DivideSharp private static Int32Divisor[] PositiveDivisors { get; } = unchecked(new Int32Divisor[] { - new Int32Divisor(3, 0x55555556, Int32DivisorStrategy.MultiplyShift, 0), - new Int32Divisor(4, 1, Int32DivisorStrategy.PowerOfTwoPositive, 2), - new Int32Divisor(5, 0x66666667, Int32DivisorStrategy.MultiplyShift, 1), - new Int32Divisor(6, 0x2aaaaaab, Int32DivisorStrategy.MultiplyShift, 0), - new Int32Divisor(7, (int)0x92492493, Int32DivisorStrategy.MultiplyAddShift, 2), - new Int32Divisor(8, 1, Int32DivisorStrategy.PowerOfTwoPositive, 3), - new Int32Divisor(9, 0x38e38e39, Int32DivisorStrategy.MultiplyShift, 1), - new Int32Divisor(10, 0x66666667, Int32DivisorStrategy.MultiplyShift, 2), - new Int32Divisor(11, 0x2e8ba2e9, Int32DivisorStrategy.MultiplyShift, 1), - new Int32Divisor(12, 0x2aaaaaab, Int32DivisorStrategy.MultiplyShift, 1), + new Int32Divisor(3, 0x55555556, SignedDivisorStrategy.MultiplyShift, 0), + new Int32Divisor(4, 1, SignedDivisorStrategy.PowerOfTwoPositive, 2), + new Int32Divisor(5, 0x66666667, SignedDivisorStrategy.MultiplyShift, 1), + new Int32Divisor(6, 0x2aaaaaab, SignedDivisorStrategy.MultiplyShift, 0), + new Int32Divisor(7, (int)0x92492493, SignedDivisorStrategy.MultiplyAddShift, 2), + new Int32Divisor(8, 1, SignedDivisorStrategy.PowerOfTwoPositive, 3), + new Int32Divisor(9, 0x38e38e39, SignedDivisorStrategy.MultiplyShift, 1), + new Int32Divisor(10, 0x66666667, SignedDivisorStrategy.MultiplyShift, 2), + new Int32Divisor(11, 0x2e8ba2e9, SignedDivisorStrategy.MultiplyShift, 1), + new Int32Divisor(12, 0x2aaaaaab, SignedDivisorStrategy.MultiplyShift, 1), }); private static Int32Divisor[] NegativeDivisors { get; } = unchecked(new Int32Divisor[] { - new Int32Divisor(-3, 0x55555555, Int32DivisorStrategy.MultiplySubtractShift, 1), - new Int32Divisor(-4, 1, Int32DivisorStrategy.PowerOfTwoNegative, 2), - new Int32Divisor(-5, (int)0x99999999, Int32DivisorStrategy.MultiplyShift, 1), - new Int32Divisor(-6, (int)0xd5555555, Int32DivisorStrategy.MultiplyShift, 0), - new Int32Divisor(-7, 0x6db6db6d, Int32DivisorStrategy.MultiplySubtractShift, 2), - new Int32Divisor(-8, 1, Int32DivisorStrategy.PowerOfTwoNegative, 3), - new Int32Divisor(-9, (int)0xc71c71c7, Int32DivisorStrategy.MultiplyShift, 1), - new Int32Divisor(-10, (int)0x99999999, Int32DivisorStrategy.MultiplyShift, 2), - new Int32Divisor(-11, (int)0xd1745d17, Int32DivisorStrategy.MultiplyShift, 1), - new Int32Divisor(-12, (int)0xd5555555, Int32DivisorStrategy.MultiplyShift, 1), + new Int32Divisor(-3, 0x55555555, SignedDivisorStrategy.MultiplySubtractShift, 1), + new Int32Divisor(-4, 1, SignedDivisorStrategy.PowerOfTwoNegative, 2), + new Int32Divisor(-5, (int)0x99999999, SignedDivisorStrategy.MultiplyShift, 1), + new Int32Divisor(-6, (int)0xd5555555, SignedDivisorStrategy.MultiplyShift, 0), + new Int32Divisor(-7, 0x6db6db6d, SignedDivisorStrategy.MultiplySubtractShift, 2), + new Int32Divisor(-8, 1, SignedDivisorStrategy.PowerOfTwoNegative, 3), + new Int32Divisor(-9, (int)0xc71c71c7, SignedDivisorStrategy.MultiplyShift, 1), + new Int32Divisor(-10, (int)0x99999999, SignedDivisorStrategy.MultiplyShift, 2), + new Int32Divisor(-11, (int)0xd1745d17, SignedDivisorStrategy.MultiplyShift, 1), + new Int32Divisor(-12, (int)0xd5555555, SignedDivisorStrategy.MultiplyShift, 1), }); - private static (int multiplier, Int32DivisorStrategy strategy, byte shift) GetMagic(int divisor) + private static (int multiplier, SignedDivisorStrategy strategy, byte shift) GetMagic(int divisor) { //Copied from CoreCLR, and modified by MineCake1.4.7 @@ -144,10 +144,10 @@ private static (int multiplier, Int32DivisorStrategy strategy, byte shift) GetMa return Math.Sign(resultMagic) != Math.Sign(divisor) #pragma warning disable S3265 // Non-flags enums should not be used in bitwise operations #pragma warning disable S3358 // Ternary operators should not be nested - ? (resultMagic, divisor > 0 ? Int32DivisorStrategy.MultiplyAddShift : Int32DivisorStrategy.MultiplySubtractShift, shift) + ? (resultMagic, divisor > 0 ? SignedDivisorStrategy.MultiplyAddShift : SignedDivisorStrategy.MultiplySubtractShift, shift) #pragma warning restore S3358 // Ternary operators should not be nested #pragma warning restore S3265 // Non-flags enums should not be used in bitwise operations - : (resultMagic, Int32DivisorStrategy.MultiplyShift, shift); + : (resultMagic, SignedDivisorStrategy.MultiplyShift, shift); } #endregion Static Members @@ -174,7 +174,7 @@ private static (int multiplier, Int32DivisorStrategy strategy, byte shift) GetMa /// /// The strategy of a division. /// - public Int32DivisorStrategy Strategy { get; } + public SignedDivisorStrategy Strategy { get; } /// /// Gets the number of bits to shift for actual "division". @@ -199,7 +199,7 @@ private static (int multiplier, Int32DivisorStrategy strategy, byte shift) GetMa /// The multiplier. /// The strategy. /// The shift. - private Int32Divisor(int divisor, int multiplier, Int32DivisorStrategy strategy, byte shift) + private Int32Divisor(int divisor, int multiplier, SignedDivisorStrategy strategy, byte shift) { Divisor = divisor; Multiplier = multiplier; @@ -220,35 +220,35 @@ public Int32Divisor(int divisor) if (divisor == 1) { Multiplier = 1; - Strategy = Int32DivisorStrategy.PowerOfTwoPositive; + Strategy = SignedDivisorStrategy.PowerOfTwoPositive; Shift = 0; Mask = 0; } else if (divisor == -1) { Multiplier = -1; - Strategy = Int32DivisorStrategy.PowerOfTwoNegative; + Strategy = SignedDivisorStrategy.PowerOfTwoNegative; Shift = 0; Mask = 0; } else if (divisor == int.MinValue) { Multiplier = 1; - Strategy = Int32DivisorStrategy.Branch; + Strategy = SignedDivisorStrategy.Branch; Shift = 0; Mask = 0; } else if ((-divisor & (-divisor - 1)) == 0) { Multiplier = 1; - Strategy = Int32DivisorStrategy.PowerOfTwoNegative; + Strategy = SignedDivisorStrategy.PowerOfTwoNegative; Shift = (byte)Utils.CountConsecutiveZeros((uint)divisor); Mask = (int)~(~0u << Shift); } else if ((divisor & (divisor - 1)) == 0) { Multiplier = 1; - Strategy = Int32DivisorStrategy.PowerOfTwoPositive; + Strategy = SignedDivisorStrategy.PowerOfTwoPositive; Shift = (byte)Utils.CountConsecutiveZeros((uint)divisor); Mask = (int)~(~0u << Shift); } @@ -268,7 +268,7 @@ public Int32Divisor(int divisor) public int Divide(int value) { uint strategy = (uint)Strategy; - if (strategy == (uint)Int32DivisorStrategy.Branch) + if (strategy == (uint)SignedDivisorStrategy.Branch) { bool al = value == int.MinValue; return Unsafe.As(ref al); @@ -318,7 +318,7 @@ public int Divide(int value) public int DivRem(int value, out int quotient) { uint strategy = (uint)Strategy; - if (strategy == (uint)Int32DivisorStrategy.Branch) + if (strategy == (uint)SignedDivisorStrategy.Branch) { bool al = value == int.MinValue; int q = quotient = Unsafe.As(ref al); @@ -372,7 +372,7 @@ public int DivRem(int value, out int quotient) public int AbsFloor(int value) { uint strategy = (uint)Strategy; - if (strategy == (uint)Int32DivisorStrategy.Branch) + if (strategy == (uint)SignedDivisorStrategy.Branch) { bool al = value == int.MinValue; return Unsafe.As(ref al) << 31; @@ -423,7 +423,7 @@ public int AbsFloor(int value) public int AbsFloorRem(int value, out int largestMultipleOfDivisor) { uint strategy = (uint)Strategy; - if (strategy == (uint)Int32DivisorStrategy.Branch) + if (strategy == (uint)SignedDivisorStrategy.Branch) { bool al = value == int.MinValue; int q = Unsafe.As(ref al); @@ -477,7 +477,7 @@ public int AbsFloorRem(int value, out int largestMultipleOfDivisor) public int Modulo(int value) { uint strategy = (uint)Strategy; - if (strategy == (uint)Int32DivisorStrategy.Branch) + if (strategy == (uint)SignedDivisorStrategy.Branch) { bool al = value != int.MinValue; return value & -Unsafe.As(ref al); diff --git a/DivideSharp/Int64Divisor.cs b/DivideSharp/Int64Divisor.cs new file mode 100644 index 0000000..c8e4f53 --- /dev/null +++ b/DivideSharp/Int64Divisor.cs @@ -0,0 +1,590 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Text; + +namespace DivideSharp +{ + /// + /// Divides an value RAPIDLY. + /// + /// + public readonly struct Int64Divisor : ISignedDivisor, IEquatable + { + #region Static Members + + private static Int64Divisor[] PositiveDivisors { get; } = unchecked(new Int64Divisor[] + { + new Int64Divisor(3, 0x5555555555555556, SignedDivisorStrategy.MultiplyShift, 0), + new Int64Divisor(4, 1, SignedDivisorStrategy.PowerOfTwoPositive, 2), + new Int64Divisor(5, 0x6666666666666667, SignedDivisorStrategy.MultiplyShift, 1), + new Int64Divisor(6, 0x2aaaaaaaaaaaaaab, SignedDivisorStrategy.MultiplyShift, 0), + new Int64Divisor(7, 0x4924924924924925, SignedDivisorStrategy.MultiplyShift, 1), + new Int64Divisor(8, 1, SignedDivisorStrategy.PowerOfTwoPositive, 3), + new Int64Divisor(9, 0x1c71c71c71c71c72, SignedDivisorStrategy.MultiplyShift, 0), + new Int64Divisor(10, 0x6666666666666667, SignedDivisorStrategy.MultiplyShift, 2), + new Int64Divisor(11, 0x2e8ba2e8ba2e8ba3, SignedDivisorStrategy.MultiplyShift, 1), + new Int64Divisor(12, 0x2aaaaaaaaaaaaaab, SignedDivisorStrategy.MultiplyShift, 1), + }); + + private static Int64Divisor[] NegativeDivisors { get; } = unchecked(new Int64Divisor[] + { + new Int64Divisor(-3, 0x5555555555555555, SignedDivisorStrategy.MultiplySubtractShift, 1), + new Int64Divisor(-4, 1, SignedDivisorStrategy.PowerOfTwoNegative, 2), + new Int64Divisor(-5, (long)0x9999999999999999, SignedDivisorStrategy.MultiplyShift, 1), + new Int64Divisor(-6, (long)0xd555555555555555, SignedDivisorStrategy.MultiplyShift, 0), + new Int64Divisor(-7, (long)0xb6db6db6db6db6db, SignedDivisorStrategy.MultiplyShift, 1), + new Int64Divisor(-8, 1, SignedDivisorStrategy.PowerOfTwoNegative, 3), + new Int64Divisor(-9, 0x1c71c71c71c71c71, SignedDivisorStrategy.MultiplySubtractShift, 3), + new Int64Divisor(-10, (long)0x9999999999999999, SignedDivisorStrategy.MultiplyShift, 2), + new Int64Divisor(-11, (long)0xd1745d1745d1745d, SignedDivisorStrategy.MultiplyShift, 1), + new Int64Divisor(-12, (long)0xd555555555555555, SignedDivisorStrategy.MultiplyShift, 1), + }); + + private static (long multiplier, SignedDivisorStrategy strategy, byte shift) GetMagic(long divisor) + { + //Copied from CoreCLR, and modified by MineCake1.4.7 + + #region License Notice + + /* + The MIT License (MIT) + Copyright (c) .NET Foundation and Contributors + All rights reserved. + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + + #endregion License Notice + + if (divisor > 0) + { + if (divisor - 3 < PositiveDivisors.Length) + { + var q = PositiveDivisors[(int)(divisor - 3)]; + return (q.Multiplier, q.Strategy, q.Shift); + } + } + else + { + if (-divisor - 3 < NegativeDivisors.Length) + { + var q = NegativeDivisors[(int)(-divisor - 3)]; + return (q.Multiplier, q.Strategy, q.Shift); + } + } + + const int Bits = sizeof(long) * 8; + const int BitsMinus1 = Bits - 1; + const ulong TwoNMinus1 = 1ul << BitsMinus1; + + int p; + ulong absDivisor; + ulong absNc; + ulong delta; + ulong q1; + ulong r1; + ulong r2; + ulong q2; + ulong t; + long resultMagic; + + absDivisor = Utils.Abs(divisor); + t = TwoNMinus1 + ((ulong)divisor >> BitsMinus1); + absNc = t - 1 - t % absDivisor; // absolute value of nc + p = BitsMinus1; // initialize p + q1 = TwoNMinus1 / absNc; // initialize q1 = 2^p / abs(nc) + r1 = TwoNMinus1 - q1 * absNc; // initialize r1 = rem(2^p, abs(nc)) + q2 = TwoNMinus1 / absDivisor; // initialize q1 = 2^p / abs(divisor) + r2 = TwoNMinus1 - q2 * absDivisor; // initialize r1 = rem(2^p, abs(divisor)) + + do + { + p++; + q1 *= 2; // update q1 = 2^p / abs(nc) + r1 *= 2; // update r1 = rem(2^p / abs(nc)) + + if (r1 >= absNc) + { // must be unsigned comparison + q1++; + r1 -= absNc; + } + + q2 *= 2; // update q2 = 2^p / abs(divisor) + r2 *= 2; // update r2 = rem(2^p / abs(divisor)) + + if (r2 >= absDivisor) + { // must be unsigned comparison + q2++; + r2 -= absDivisor; + } + + delta = absDivisor - r2; + } while (q1 < delta || q1 == delta && r1 == 0); + + resultMagic = (long)(q2 + 1); // resulting magic number + if (divisor < 0) + { + resultMagic = -resultMagic; + } + byte shift = (byte)(p - Bits); // resulting shift + return Math.Sign(resultMagic) != Math.Sign(divisor) +#pragma warning disable S3265 // Non-flags enums should not be used in bitwise operations +#pragma warning disable S3358 // Ternary operators should not be nested + ? + (resultMagic, divisor > 0 ? SignedDivisorStrategy.MultiplyAddShift : SignedDivisorStrategy.MultiplySubtractShift, shift) +#pragma warning restore S3358 // Ternary operators should not be nested +#pragma warning restore S3265 // Non-flags enums should not be used in bitwise operations + : + (resultMagic, SignedDivisorStrategy.MultiplyShift, shift); + } + + #endregion Static Members + + /// + /// Gets the divisor. + /// + /// + /// The divisor. + /// + public long Divisor { get; } + + /// + /// Gets the multiplier for actual "division". + /// + /// + /// The multiplier. + /// + public long Multiplier { get; } + + /// + /// Gets the strategy of a division. + /// + /// + /// The strategy of a division. + /// + public SignedDivisorStrategy Strategy { get; } + + /// + /// Gets the number of bits to shift for actual "division". + /// + /// + /// The number of bits to shift right. + /// + public byte Shift { get; } + + /// + /// Gets the mask to divide. + /// + /// + /// The mask. + /// + public long Mask { get; } + + /// + /// Initializes a new instance of the struct. + /// + /// The divisor. + /// The multiplier. + /// The strategy. + /// The shift. + private Int64Divisor(long divisor, long multiplier, SignedDivisorStrategy strategy, byte shift) + { + Divisor = divisor; + Multiplier = multiplier; + Strategy = strategy; + Shift = shift; + Mask = (long)~(~0u << shift); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The divisor. + /// + public Int64Divisor(long divisor) + { + if (divisor == 0) throw new DivideByZeroException(); + Divisor = divisor; + if (divisor == 1) + { + Multiplier = 1; + Strategy = SignedDivisorStrategy.PowerOfTwoPositive; + Shift = 0; + Mask = 0; + } + else if (divisor == -1) + { + Multiplier = -1; + Strategy = SignedDivisorStrategy.PowerOfTwoNegative; + Shift = 0; + Mask = 0; + } + else if (divisor == long.MinValue) + { + Multiplier = 1; + Strategy = SignedDivisorStrategy.Branch; + Shift = 0; + Mask = 0; + } + else if ((-divisor & (-divisor - 1)) == 0) + { + Multiplier = 1; + Strategy = SignedDivisorStrategy.PowerOfTwoNegative; + Shift = (byte)Utils.CountConsecutiveZeros((ulong)divisor); + Mask = (long)~(~0u << Shift); + } + else if ((divisor & (divisor - 1)) == 0) + { + Multiplier = 1; + Strategy = SignedDivisorStrategy.PowerOfTwoPositive; + Shift = (byte)Utils.CountConsecutiveZeros((ulong)divisor); + Mask = (long)~(~0u << Shift); + } + else + { + (Multiplier, Strategy, Shift) = GetMagic(divisor); + Mask = (long)~(~0u << Shift); + } + } + + /// + /// Divides the specified by . + /// + /// The dividend. + /// The value divided by . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long Divide(long value) + { + uint strategy = (uint)Strategy; + if (strategy == (uint)SignedDivisorStrategy.Branch) + { + bool al = value == long.MinValue; + return Unsafe.As(ref al); + } + long rax = value; + ulong r9; + if ((strategy & 0b100u) > 0) + { + long multiplier = Multiplier; + int shift = Shift; + rax = Utils.MultiplyHigh(rax, multiplier); + if ((strategy & 0b001u) > 0) + { + rax -= value; + } + else if ((strategy & 0b010u) > 0) + { + rax += value; + } + r9 = (ulong)rax; + r9 >>= 63; + rax >>= shift; + return rax + (long)r9; + } + else + { + rax >>= 63; + long mask = Mask; + int shift = Shift; + strategy &= 0b1u; + rax &= mask; + rax += value; + rax >>= shift; + return ((strategy & 0b1u) > 0) ? -rax : rax; + } + } + + /// + /// Calculates the quotient of two 32-bit signed integers ( and ) and the remainder. + /// + /// The dividend. + /// The quotient of the specified numbers. + /// The remainder. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long DivRem(long value, out long quotient) + { + uint strategy = (uint)Strategy; + if (strategy == (uint)SignedDivisorStrategy.Branch) + { + bool al = value == long.MinValue; + long q = quotient = Unsafe.As(ref al); + return value & --q; + } + long rax = value; + ulong rdx; + if ((strategy & 0b100u) > 0) + { + long multiplier = Multiplier; + long divisor = Divisor; + int shift = Shift; + rax = Utils.MultiplyHigh(rax, multiplier); + if ((strategy & 0b001u) > 0) + { + rax -= value; + } + else if ((strategy & 0b010u) > 0) + { + rax += value; + } + rdx = (ulong)rax; + rdx >>= 63; + rax >>= shift; + long r11 = quotient = rax + (long)rdx; + return value - r11 * divisor; + } + else + { + rax >>= 63; + long mask = Mask; + int shift = Shift; + rax &= mask; + rax += value; + long r11 = rax >> shift; + mask = ~mask; + quotient = ((strategy & 0b1u) > 0) ? -r11 : r11; + mask &= rax; + return value - mask; + } + } + + /// + /// Returns a multiple of that has the largest absolute value less than the absolute value of the specified . + /// + /// The dividend. + /// A multiple of that has the largest absolute value less than the absolute value of the specified . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long AbsFloor(long value) + { + uint strategy = (uint)Strategy; + if (strategy == (uint)SignedDivisorStrategy.Branch) + { + bool al = value == long.MinValue; + return (long)Unsafe.As(ref al) << 63; + } + long rax = value; + ulong r9; + if ((strategy & 0b100u) > 0) + { + long divisor = Divisor; + long multiplier = Multiplier; + int shift = Shift; + rax = Utils.MultiplyHigh(rax, multiplier); + if ((strategy & 0b001u) > 0) + { + rax -= value; + } + else if ((strategy & 0b010u) > 0) + { + rax += value; + } + r9 = (ulong)rax; + r9 >>= 63; + rax >>= shift; + rax += (long)r9; + return rax * divisor; + } + else + { + rax >>= 63; + long mask = Mask; + rax &= mask; + rax += value; + mask = ~mask; + mask &= rax; + return mask; + } + } + + /// + /// Calculates a multiple of that has the largest absolute value less than the absolute value of the specified and stores it in , and Returns the difference between and . + /// + /// The value. + /// A multiple of that has the largest absolute value less than the absolute value of the specified . + /// The difference between and . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long AbsFloorRem(long value, out long largestMultipleOfDivisor) + { + uint strategy = (uint)Strategy; + if (strategy == (uint)SignedDivisorStrategy.Branch) + { + bool al = value == long.MinValue; + long q = Unsafe.As(ref al); + largestMultipleOfDivisor = q << 63; + return value & --q; + } + long rax = value; + ulong r9; + if ((strategy & 0b100u) > 0) + { + long divisor = Divisor; + long multiplier = Multiplier; + int shift = Shift; + rax = Utils.MultiplyHigh(rax, multiplier); + if ((strategy & 0b001u) > 0) + { + rax -= value; + } + else if ((strategy & 0b010u) > 0) + { + rax += value; + } + r9 = (ulong)rax; + r9 >>= 63; + rax >>= shift; + rax += (long)r9; + var f = largestMultipleOfDivisor = rax * divisor; + return value - f; + } + else + { + rax >>= 63; + long mask = Mask; + rax &= mask; + rax += value; + mask = ~mask; + mask &= rax; + largestMultipleOfDivisor = mask; + return value - mask; + } + } + + /// + /// Calculates the remainder by of the specified . + /// + /// The dividend. + /// The remainder. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long Modulo(long value) + { + uint strategy = (uint)Strategy; + if (strategy == (uint)SignedDivisorStrategy.Branch) + { + bool al = value != long.MinValue; + return value & -Unsafe.As(ref al); + } + long rax = value; + ulong rdx; + if ((strategy & 0b100u) > 0) + { + long multiplier = Multiplier; + long divisor = Divisor; + int shift = Shift; + rax = Utils.MultiplyHigh(rax, multiplier); + if ((strategy & 0b001u) > 0) + { + rax -= value; + } + else if ((strategy & 0b010u) > 0) + { + rax += value; + } + rdx = (ulong)rax; + rdx >>= 63; + rax >>= shift; + long r11 = rax + (long)rdx; + return value - r11 * divisor; + } + else + { + rax >>= 63; + long mask = Mask; + rax &= mask; + rax += value; + mask = ~mask; + mask &= rax; + return value - mask; + } + } + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// An object to compare with this object. + /// + /// true if the current object is equal to the obj parameter; otherwise, false. + /// + public override bool Equals(object obj) => obj is Int64Divisor divisor && Equals(divisor); + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// An object to compare with this object. + /// + /// true if the current object is equal to the other parameter; otherwise, false. + /// + public bool Equals(Int64Divisor other) => Divisor == other.Divisor && Multiplier == other.Multiplier && Strategy == other.Strategy && Shift == other.Shift && Mask == other.Mask; + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + int hashCode = 1864125951; + hashCode = hashCode * -1521134295 + Divisor.GetHashCode(); + hashCode = hashCode * -1521134295 + Multiplier.GetHashCode(); + hashCode = hashCode * -1521134295 + Strategy.GetHashCode(); + hashCode = hashCode * -1521134295 + Shift.GetHashCode(); + hashCode = hashCode * -1521134295 + Mask.GetHashCode(); + return hashCode; + } + + #region Operators + + /// + /// Rapidly divides the specified value with 's . + /// + /// The dividend. + /// The divisor. + /// The result of dividing by . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long operator /(long left, Int64Divisor right) => right.Divide(left); + + /// + /// Rapidly returns the remainder resulting from dividing the specified value with 's . + /// + /// The dividend. + /// The divisor. + /// The remainder resulting from dividing by . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long operator %(long left, Int64Divisor right) => right.Modulo(left); + + /// + /// Indicates whether the values of two specified objects are equal. + /// + /// The first to compare. + /// The second to compare. + /// + /// true if the left is the same as the right; otherwise, false. + /// + public static bool operator ==(Int64Divisor left, Int64Divisor right) => left.Equals(right); + + /// + /// Indicates whether the values of two specified objects are not equal. + /// + /// The first to compare. + /// The second to compare. + /// + /// true if left and right are not equal; otherwise, false. + /// + public static bool operator !=(Int64Divisor left, Int64Divisor right) => !(left == right); + + #endregion Operators + } +} diff --git a/DivideSharp/UInt64Divisor.cs b/DivideSharp/UInt64Divisor.cs index 1d8182c..fb69ef8 100644 --- a/DivideSharp/UInt64Divisor.cs +++ b/DivideSharp/UInt64Divisor.cs @@ -215,7 +215,7 @@ public ulong Divide(ulong value) return Unsafe.As(ref v); } ulong rax = value; - ulong r8d = value; + ulong r8 = value; ulong multiplier = Multiplier; int shift = Shift; if ((strategy & 0b10u) > 0) @@ -223,9 +223,9 @@ public ulong Divide(ulong value) rax = Utils.MultiplyHigh(rax, multiplier); if ((strategy & 0b01u) > 0) { - r8d -= rax; - r8d >>= 1; - rax += r8d; + r8 -= rax; + r8 >>= 1; + rax += r8; } } rax >>= shift; @@ -242,8 +242,8 @@ public ulong Divide(ulong value) public ulong DivRem(ulong value, out ulong quotient) { ulong rax = value; - ulong r8d; - ulong r9d = value; + ulong r8; + ulong r9 = value; ulong multiplier = Multiplier; uint strategy = (uint)Strategy; ulong divisor = Divisor; @@ -255,22 +255,22 @@ public ulong DivRem(ulong value, out ulong quotient) rax = Utils.MultiplyHigh(rax, multiplier); if ((strategy & 0b01u) > 0) { - r8d = value; - r8d -= rax; - r8d >>= 1; - rax += r8d; + r8 = value; + r8 -= rax; + r8 >>= 1; + rax += r8; } - r8d = rax >> shift; - quotient = r8d; - r8d *= divisor; - return value - r8d; + r8 = rax >> shift; + quotient = r8; + r8 *= divisor; + return value - r8; } else { - r9d >>= shift; - quotient = r9d; - r9d <<= shift; - return value ^ r9d; + r9 >>= shift; + quotient = r9; + r9 <<= shift; + return value ^ r9; } } else @@ -300,26 +300,26 @@ public ulong Floor(ulong value) return divisor & (ulong)-Unsafe.As(ref v); } ulong rax = value; - ulong r8d; + ulong r8; ulong multiplier = Multiplier; int shift = Shift; if ((strategy & 0b10u) == 0) { - r8d = ~0ul << shift; - return rax & r8d; + r8 = ~0ul << shift; + return rax & r8; } else { rax = Utils.MultiplyHigh(rax, multiplier); if ((strategy & 0b01u) > 0) { - r8d = value; - r8d -= rax; - r8d >>= 1; - rax += r8d; + r8 = value; + r8 -= rax; + r8 >>= 1; + rax += r8; } - r8d = rax >> shift; - return r8d * divisor; + r8 = rax >> shift; + return r8 * divisor; } } @@ -333,7 +333,7 @@ public ulong Floor(ulong value) public ulong FloorRem(ulong value, out ulong largestMultipleOfDivisor) { ulong rax = value; - ulong r8d; + ulong r8; ulong multiplier = Multiplier; uint strategy = (uint)Strategy; ulong divisor = Divisor; @@ -345,29 +345,29 @@ public ulong FloorRem(ulong value, out ulong largestMultipleOfDivisor) rax = Utils.MultiplyHigh(rax, multiplier); if ((strategy & 0b01u) > 0) { - r8d = value; - r8d -= rax; - r8d >>= 1; - rax += r8d; + r8 = value; + r8 -= rax; + r8 >>= 1; + rax += r8; } - r8d = rax >> shift; - r8d *= divisor; - largestMultipleOfDivisor = r8d; - return value - r8d; + r8 = rax >> shift; + r8 *= divisor; + largestMultipleOfDivisor = r8; + return value - r8; } else { - var r9d = ~0ul << shift; - largestMultipleOfDivisor = value & r9d; - return value & ~r9d; + var r9 = ~0ul << shift; + largestMultipleOfDivisor = value & r9; + return value & ~r9; } } else { bool v = value >= divisor; - r8d = (divisor & (ulong)-Unsafe.As(ref v)); - largestMultipleOfDivisor = r8d; - return value - r8d; + r8 = (divisor & (ulong)-Unsafe.As(ref v)); + largestMultipleOfDivisor = r8; + return value - r8; } } @@ -381,7 +381,7 @@ public ulong FloorRem(ulong value, out ulong largestMultipleOfDivisor) public ulong Modulo(ulong value) { ulong rax = value; - ulong r8d = value; + ulong r8 = value; ulong multiplier = Multiplier; uint strategy = (uint)Strategy; ulong divisor = Divisor; @@ -393,13 +393,13 @@ public ulong Modulo(ulong value) rax = Utils.MultiplyHigh(rax, multiplier); if ((strategy & 0b01u) > 0) { - r8d -= rax; - r8d >>= 1; - rax += r8d; + r8 -= rax; + r8 >>= 1; + rax += r8; } - r8d = rax >> shift; - r8d *= divisor; - return value - r8d; + r8 = rax >> shift; + r8 *= divisor; + return value - r8; } else { diff --git a/DivideSharp/Utils.cs b/DivideSharp/Utils.cs index 74675cc..026b9da 100644 --- a/DivideSharp/Utils.cs +++ b/DivideSharp/Utils.cs @@ -190,17 +190,26 @@ public static int CountConsecutiveZeros(uint value) /// The x. /// The y. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long MultiplyHigh(long x, long y) { - ulong u = (uint)x * (ulong)(uint)y; - long s = (long)(u >> 32); - long xhi = x >> 32; - long yhi = y >> 32; - s += xhi * (uint)y; - s += yhi * (uint)x; - s >>= 32; - s += xhi * yhi; - return s; + unchecked + { + ulong xl = (uint)x; + ulong yl = (uint)y; + long xh = x >> 32; + long yh = y >> 32; + ulong u = xl * yl; + long s = (long)(u >> 32); + long t1 = xh * (long)yl; + long t2 = yh * (long)xl; + s += (uint)t1; + s += (uint)t2; + s >>= 32; + s += (t1 >> 32) + (t2 >> 32); + s += xh * yh; + return s; + } } /// @@ -250,6 +259,17 @@ public static ulong MultiplyHigh(ulong x, ulong y) return xh * yh + (t >> 32) + (tl >> 32); } + /// + /// Returns the absolute value of the specified . + /// + /// The value. + /// + public static ushort Abs(short value) + { + var q = value >> 15; + return (ushort)((value + q) ^ q); + } + /// /// Returns the absolute value of the specified . /// @@ -260,5 +280,16 @@ public static uint Abs(int value) var q = value >> 31; return (uint)((value + q) ^ q); } + + /// + /// Returns the absolute value of the specified . + /// + /// The value. + /// + public static ulong Abs(long value) + { + var q = value >> 63; + return (ulong)((value + q) ^ q); + } } } diff --git a/DivisionBenchmark/Program.cs b/DivisionBenchmark/Program.cs index 1a13bba..bd34a7d 100644 --- a/DivisionBenchmark/Program.cs +++ b/DivisionBenchmark/Program.cs @@ -8,7 +8,7 @@ internal class Program { private static void Main(string[] args) { - BenchmarkRunner.Run(); + BenchmarkRunner.Run(); } } } diff --git a/DivisionBenchmark/SignBenchmarks.cs b/DivisionBenchmark/SignBenchmarks.cs index ada9cef..e78c6e3 100644 --- a/DivisionBenchmark/SignBenchmarks.cs +++ b/DivisionBenchmark/SignBenchmarks.cs @@ -6,15 +6,23 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Jobs; +using TestAndBenchmarkUtils; + namespace DivisionBenchmark { [SimpleJob(RuntimeMoniker.HostProcess)] public class SignBenchmarks { - private int a = 0; + private PcgRandom rng; + + [GlobalSetup] + public void Setup() + { + rng = new PcgRandom(); + } //[MethodImpl(MethodImplOptions.NoInlining)] - public int ValueToBeTested() => a += int.MaxValue; + public int ValueToBeTested() => unchecked((int)rng.Next()); [Benchmark(Baseline = true)] public int Standard() => Math.Sign(ValueToBeTested()); @@ -42,5 +50,22 @@ public int ShiftOr() var cl = v != 0; return Unsafe.As(ref cl) | (v >> 31); } + + /* + // * Summary * + + BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18363 + Intel Core i7-4790 CPU 3.60GHz (Haswell), 1 CPU, 8 logical and 4 physical cores + .NET Core SDK=3.1.402 + [Host] : .NET Core 3.1.8 (CoreCLR 4.700.20.41105, CoreFX 4.700.20.41903), X64 RyuJIT + DefaultJob : .NET Core 3.1.8 (CoreCLR 4.700.20.41105, CoreFX 4.700.20.41903), X64 RyuJIT + + | Method | Mean | Error | StdDev | Ratio | RatioSD | + |------------------- |---------:|----------:|----------:|------:|--------:| + | Standard | 2.691 ns | 0.0854 ns | 0.0949 ns | 1.00 | 0.00 | + | JustRShift31 | 2.492 ns | 0.0809 ns | 0.1284 ns | 0.92 | 0.05 | + | CompareSetSubtract | 3.005 ns | 0.1057 ns | 0.2385 ns | 1.12 | 0.15 | + | ShiftOr | 2.843 ns | 0.0922 ns | 0.1540 ns | 1.06 | 0.07 | + */ } }