Add PopCount for built-in integer types

This commit is contained in:
Oliver Booth 2022-07-08 13:09:34 +01:00
parent 7231c41466
commit b1eadf61f4
No known key found for this signature in database
GPG Key ID: 32A00B35503AF634
17 changed files with 332 additions and 0 deletions

View File

@ -14,6 +14,7 @@
- X10D: Added `Point.ToVector2()` - X10D: Added `Point.ToVector2()`
- X10D: Added `PointF.ToSizeF()` - X10D: Added `PointF.ToSizeF()`
- X10D: Added `PointF.ToVector2()` for .NET < 6 - X10D: Added `PointF.ToVector2()` for .NET < 6
- X10D: Added `PopCount()` for built-in integer types
- X10D: Added `RoundUpToPowerOf2()` for built-in integer types - X10D: Added `RoundUpToPowerOf2()` for built-in integer types
- X10D: Added `Size.ToPoint()` - X10D: Added `Size.ToPoint()`
- X10D: Added `Size.ToPointF()` - X10D: Added `Size.ToPointF()`

View File

@ -6,6 +6,24 @@ namespace X10D.Tests.Numerics;
[TestClass] [TestClass]
public class ByteTests public class ByteTests
{ {
[TestMethod]
public void PopCount_ShouldBe0_Given0()
{
Assert.AreEqual(0, ((byte)0).PopCount());
}
[TestMethod]
public void PopCount_ShouldBe5_Given11010101()
{
Assert.AreEqual(5, ((byte)0b11010101).PopCount());
}
[TestMethod]
public void PopCount_ShouldBe8_Given11111111()
{
Assert.AreEqual(8, ((byte)0b11111111).PopCount());
}
[TestMethod] [TestMethod]
public void RotateLeft_ShouldRotateCorrectly() public void RotateLeft_ShouldRotateCorrectly()
{ {

View File

@ -6,6 +6,24 @@ namespace X10D.Tests.Numerics;
[TestClass] [TestClass]
public class Int16Tests public class Int16Tests
{ {
[TestMethod]
public void PopCount_ShouldBe0_Given0()
{
Assert.AreEqual(0, ((short)0).PopCount());
}
[TestMethod]
public void PopCount_ShouldBe5_Given11010101()
{
Assert.AreEqual(5, ((short)0b11010101).PopCount());
}
[TestMethod]
public void PopCount_ShouldBe15_Given0111111111111111()
{
Assert.AreEqual(15, ((short)0b0111111111111111).PopCount());
}
[TestMethod] [TestMethod]
public void RotateLeft_ShouldRotateCorrectly() public void RotateLeft_ShouldRotateCorrectly()
{ {

View File

@ -6,6 +6,24 @@ namespace X10D.Tests.Numerics;
[TestClass] [TestClass]
public class Int32Tests public class Int32Tests
{ {
[TestMethod]
public void PopCount_ShouldBe0_Given0()
{
Assert.AreEqual(0, ((uint)0).PopCount());
}
[TestMethod]
public void PopCount_ShouldBe5_Given11010101()
{
Assert.AreEqual(5, ((uint)0b11010101).PopCount());
}
[TestMethod]
public void PopCount_ShouldBe31_Given11111111111111111111111111111111()
{
Assert.AreEqual(31, 0b01111111111111111111111111111111.PopCount());
}
[TestMethod] [TestMethod]
public void RotateLeft_ShouldRotateCorrectly() public void RotateLeft_ShouldRotateCorrectly()
{ {

View File

@ -6,6 +6,24 @@ namespace X10D.Tests.Numerics;
[TestClass] [TestClass]
public class Int64Tests public class Int64Tests
{ {
[TestMethod]
public void PopCount_ShouldBe0_Given0()
{
Assert.AreEqual(0, 0L.PopCount());
}
[TestMethod]
public void PopCount_ShouldBe5_Given11010101()
{
Assert.AreEqual(5, 0b11010101L.PopCount());
}
[TestMethod]
public void PopCount_ShouldBe63_Given0111111111111111111111111111111111111111111111111111111111111111()
{
Assert.AreEqual(63, 0b0111111111111111111111111111111111111111111111111111111111111111L.PopCount());
}
[TestMethod] [TestMethod]
public void RotateLeft_ShouldRotateCorrectly() public void RotateLeft_ShouldRotateCorrectly()
{ {

View File

@ -7,6 +7,24 @@ namespace X10D.Tests.Numerics;
[CLSCompliant(false)] [CLSCompliant(false)]
public class SByteTests public class SByteTests
{ {
[TestMethod]
public void PopCount_ShouldBe0_Given0()
{
Assert.AreEqual(0, ((sbyte)0).PopCount());
}
[TestMethod]
public void PopCount_ShouldBe4_Given01010101()
{
Assert.AreEqual(4, ((sbyte)0b01010101).PopCount());
}
[TestMethod]
public void PopCount_ShouldBe7_Given01111111()
{
Assert.AreEqual(7, ((sbyte)0b01111111).PopCount());
}
[TestMethod] [TestMethod]
public void RotateLeft_ShouldRotateCorrectly() public void RotateLeft_ShouldRotateCorrectly()
{ {

View File

@ -7,6 +7,24 @@ namespace X10D.Tests.Numerics;
[CLSCompliant(false)] [CLSCompliant(false)]
public class UInt16Tests public class UInt16Tests
{ {
[TestMethod]
public void PopCount_ShouldBe0_Given0()
{
Assert.AreEqual(0, ((ushort)0).PopCount());
}
[TestMethod]
public void PopCount_ShouldBe5_Given11010101()
{
Assert.AreEqual(5, ((ushort)0b11010101).PopCount());
}
[TestMethod]
public void PopCount_ShouldBe16_Given1111111111111111()
{
Assert.AreEqual(16, ((ushort)0b1111111111111111).PopCount());
}
[TestMethod] [TestMethod]
public void RotateLeft_ShouldRotateCorrectly() public void RotateLeft_ShouldRotateCorrectly()
{ {

View File

@ -7,6 +7,24 @@ namespace X10D.Tests.Numerics;
[CLSCompliant(false)] [CLSCompliant(false)]
public class UInt32Tests public class UInt32Tests
{ {
[TestMethod]
public void PopCount_ShouldBe0_Given0()
{
Assert.AreEqual(0, 0U.PopCount());
}
[TestMethod]
public void PopCount_ShouldBe5_Given11010101()
{
Assert.AreEqual(5, 0b11010101U.PopCount());
}
[TestMethod]
public void PopCount_ShouldBe32_Given11111111111111111111111111111111()
{
Assert.AreEqual(32, 0b11111111111111111111111111111111U.PopCount());
}
[TestMethod] [TestMethod]
public void RotateLeft_ShouldRotateCorrectly() public void RotateLeft_ShouldRotateCorrectly()
{ {

View File

@ -7,6 +7,24 @@ namespace X10D.Tests.Numerics;
[CLSCompliant(false)] [CLSCompliant(false)]
public class UInt64Tests public class UInt64Tests
{ {
[TestMethod]
public void PopCount_ShouldBe0_Given0()
{
Assert.AreEqual(0, 0UL.PopCount());
}
[TestMethod]
public void PopCount_ShouldBe5_Given11010101()
{
Assert.AreEqual(5, 0b11010101UL.PopCount());
}
[TestMethod]
public void PopCount_ShouldBe64_Given1111111111111111111111111111111111111111111111111111111111111111()
{
Assert.AreEqual(64, 0b1111111111111111111111111111111111111111111111111111111111111111UL.PopCount());
}
[TestMethod] [TestMethod]
public void RotateLeft_ShouldRotateCorrectly() public void RotateLeft_ShouldRotateCorrectly()
{ {

View File

@ -9,6 +9,26 @@ namespace X10D.Numerics;
/// </summary> /// </summary>
public static class ByteExtensions public static class ByteExtensions
{ {
/// <summary>
/// Returns the population count (number of bits set) of a mask.
/// </summary>
/// <param name="value">The mask.</param>
/// <returns>The population count of <paramref name="value" />.</returns>
/// <remarks>
/// This method is similar in behavior to the x86 instruction
/// <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.intrinsics.x86.popcnt?view=net-6.0">POPCNT</a>
/// </remarks>
[Pure]
#if NETSTANDARD2_1
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#endif
public static int PopCount(this byte value)
{
return ((uint)value).PopCount();
}
/// <summary> /// <summary>
/// Rotates the current value left by the specified number of bits. /// Rotates the current value left by the specified number of bits.
/// </summary> /// </summary>

View File

@ -8,6 +8,26 @@ namespace X10D.Numerics;
/// </summary> /// </summary>
public static class Int16Extensions public static class Int16Extensions
{ {
/// <summary>
/// Returns the population count (number of bits set) of a mask.
/// </summary>
/// <param name="value">The mask.</param>
/// <returns>The population count of <paramref name="value" />.</returns>
/// <remarks>
/// This method is similar in behavior to the x86 instruction
/// <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.intrinsics.x86.popcnt?view=net-6.0">POPCNT</a>
/// </remarks>
[Pure]
#if NETSTANDARD2_1
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#endif
public static int PopCount(this short value)
{
return ((uint)value).PopCount();
}
/// <summary> /// <summary>
/// Rotates the current value left by the specified number of bits. /// Rotates the current value left by the specified number of bits.
/// </summary> /// </summary>

View File

@ -8,6 +8,26 @@ namespace X10D.Numerics;
/// </summary> /// </summary>
public static class Int32Extensions public static class Int32Extensions
{ {
/// <summary>
/// Returns the population count (number of bits set) of a mask.
/// </summary>
/// <param name="value">The mask.</param>
/// <returns>The population count of <paramref name="value" />.</returns>
/// <remarks>
/// This method is similar in behavior to the x86 instruction
/// <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.intrinsics.x86.popcnt?view=net-6.0">POPCNT</a>
/// </remarks>
[Pure]
#if NETSTANDARD2_1
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#endif
public static int PopCount(this int value)
{
return ((uint)value).PopCount();
}
/// <summary> /// <summary>
/// Rotates the current value left by the specified number of bits. /// Rotates the current value left by the specified number of bits.
/// </summary> /// </summary>

View File

@ -8,6 +8,26 @@ namespace X10D.Numerics;
/// </summary> /// </summary>
public static class Int64Extensions public static class Int64Extensions
{ {
/// <summary>
/// Returns the population count (number of bits set) of a mask.
/// </summary>
/// <param name="value">The mask.</param>
/// <returns>The population count of <paramref name="value" />.</returns>
/// <remarks>
/// This method is similar in behavior to the x86 instruction
/// <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.intrinsics.x86.popcnt?view=net-6.0">POPCNT</a>
/// </remarks>
[Pure]
#if NETSTANDARD2_1
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#endif
public static int PopCount(this long value)
{
return ((ulong)value).PopCount();
}
/// <summary> /// <summary>
/// Rotates the current value left by the specified number of bits. /// Rotates the current value left by the specified number of bits.
/// </summary> /// </summary>

View File

@ -9,6 +9,26 @@ namespace X10D.Numerics;
[CLSCompliant(false)] [CLSCompliant(false)]
public static class SByteExtensions public static class SByteExtensions
{ {
/// <summary>
/// Returns the population count (number of bits set) of a mask.
/// </summary>
/// <param name="value">The mask.</param>
/// <returns>The population count of <paramref name="value" />.</returns>
/// <remarks>
/// This method is similar in behavior to the x86 instruction
/// <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.intrinsics.x86.popcnt?view=net-6.0">POPCNT</a>
/// </remarks>
[Pure]
#if NETSTANDARD2_1
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#endif
public static int PopCount(this sbyte value)
{
return ((uint)value).PopCount();
}
/// <summary> /// <summary>
/// Rotates the current value left by the specified number of bits. /// Rotates the current value left by the specified number of bits.
/// </summary> /// </summary>

View File

@ -1,4 +1,5 @@
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace X10D.Numerics; namespace X10D.Numerics;
@ -9,6 +10,26 @@ namespace X10D.Numerics;
[CLSCompliant(false)] [CLSCompliant(false)]
public static class UInt16Extensions public static class UInt16Extensions
{ {
/// <summary>
/// Returns the population count (number of bits set) of a mask.
/// </summary>
/// <param name="value">The mask.</param>
/// <returns>The population count of <paramref name="value" />.</returns>
/// <remarks>
/// This method is similar in behavior to the x86 instruction
/// <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.intrinsics.x86.popcnt?view=net-6.0">POPCNT</a>
/// </remarks>
[Pure]
#if NETSTANDARD2_1
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#endif
public static int PopCount(this ushort value)
{
return ((uint)value).PopCount();
}
/// <summary> /// <summary>
/// Rotates the current value left by the specified number of bits. /// Rotates the current value left by the specified number of bits.
/// </summary> /// </summary>

View File

@ -10,6 +10,39 @@ namespace X10D.Numerics;
[CLSCompliant(false)] [CLSCompliant(false)]
public static class UInt32Extensions public static class UInt32Extensions
{ {
/// <summary>
/// Returns the population count (number of bits set) of a mask.
/// </summary>
/// <param name="value">The mask.</param>
/// <returns>The population count of <paramref name="value" />.</returns>
/// <remarks>
/// This method is similar in behavior to the x86 instruction
/// <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.intrinsics.x86.popcnt?view=net-6.0">POPCNT</a>
/// </remarks>
[Pure]
#if NETSTANDARD2_1
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#endif
public static int PopCount(this uint value)
{
#if NETCOREAPP3_1_OR_GREATER
return BitOperations.PopCount(value);
#else
const uint c1 = 0x_55555555u;
const uint c2 = 0x_33333333u;
const uint c3 = 0x_0F0F0F0Fu;
const uint c4 = 0x_01010101u;
value -= (value >> 1) & c1;
value = (value & c2) + ((value >> 2) & c2);
value = (((value + (value >> 4)) & c3) * c4) >> 24;
return (int)value;
#endif
}
/// <summary> /// <summary>
/// Rotates the current value left by the specified number of bits. /// Rotates the current value left by the specified number of bits.
/// </summary> /// </summary>

View File

@ -10,6 +10,39 @@ namespace X10D.Numerics;
[CLSCompliant(false)] [CLSCompliant(false)]
public static class UInt64Extensions public static class UInt64Extensions
{ {
/// <summary>
/// Returns the population count (number of bits set) of a mask.
/// </summary>
/// <param name="value">The mask.</param>
/// <returns>The population count of <paramref name="value" />.</returns>
/// <remarks>
/// This method is similar in behavior to the x86 instruction
/// <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.intrinsics.x86.popcnt?view=net-6.0">POPCNT</a>
/// </remarks>
[Pure]
#if NETSTANDARD2_1
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#endif
public static int PopCount(this ulong value)
{
#if NETCOREAPP3_1_OR_GREATER
return BitOperations.PopCount(value);
#else
const uint c1 = 0x_55555555u;
const uint c2 = 0x_33333333u;
const uint c3 = 0x_0F0F0F0Fu;
const uint c4 = 0x_01010101u;
value -= (value >> 1) & c1;
value = (value & c2) + ((value >> 2) & c2);
value = (((value + (value >> 4)) & c3) * c4) >> 24;
return (int)value;
#endif
}
/// <summary> /// <summary>
/// Rotates the current value left by the specified number of bits. /// Rotates the current value left by the specified number of bits.
/// </summary> /// </summary>