From 683e02cc2a287883d15ca8f2d3b52bd203e3fc4c Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Wed, 18 May 2022 11:55:47 +0100 Subject: [PATCH] Add RoundUpToPowerOf2 --- CHANGELOG.md | 1 + X10D.Tests/src/Numerics/ByteTests.cs | 25 ++++++++++++++++++++ X10D.Tests/src/Numerics/Int16Tests.cs | 25 ++++++++++++++++++++ X10D.Tests/src/Numerics/Int32Tests.cs | 25 ++++++++++++++++++++ X10D.Tests/src/Numerics/Int64Tests.cs | 25 ++++++++++++++++++++ X10D.Tests/src/Numerics/SByteTests.cs | 25 ++++++++++++++++++++ X10D.Tests/src/Numerics/UInt16Tests.cs | 25 ++++++++++++++++++++ X10D.Tests/src/Numerics/UInt32Tests.cs | 23 ++++++++++++++++++ X10D.Tests/src/Numerics/UInt64Tests.cs | 25 ++++++++++++++++++++ X10D/src/Numerics/ByteExtensions.cs | 19 +++++++++++++++ X10D/src/Numerics/Int16Extensions.cs | 19 +++++++++++++++ X10D/src/Numerics/Int32Extensions.cs | 19 +++++++++++++++ X10D/src/Numerics/Int64Extensions.cs | 19 +++++++++++++++ X10D/src/Numerics/SByteExtensions.cs | 19 +++++++++++++++ X10D/src/Numerics/UInt16Extensions.cs | 19 +++++++++++++++ X10D/src/Numerics/UInt32Extensions.cs | 31 +++++++++++++++++++++++++ X10D/src/Numerics/UInt64Extensions.cs | 32 ++++++++++++++++++++++++++ 17 files changed, 376 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cf81f3..407ce5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 3.2.0 ### Added +- X10D: Added `RoundUpToPowerOf2()` for built-in integer types - X10D: Added `Vector2.Deconstruct()` - X10D: Added `Vector3.Deconstruct()` - X10D: Added `Vector4.Deconstruct()` diff --git a/X10D.Tests/src/Numerics/ByteTests.cs b/X10D.Tests/src/Numerics/ByteTests.cs index d4890b2..c9cf215 100644 --- a/X10D.Tests/src/Numerics/ByteTests.cs +++ b/X10D.Tests/src/Numerics/ByteTests.cs @@ -39,4 +39,29 @@ public class ByteTests const byte value = 181; // 10110101 Assert.AreEqual(value, value.RotateRight(8)); } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturnRoundedValue_GivenValue() + { + Assert.AreEqual(4, ((byte)3).RoundUpToPowerOf2()); + Assert.AreEqual(8, ((byte)5).RoundUpToPowerOf2()); + Assert.AreEqual(8, ((byte)6).RoundUpToPowerOf2()); + Assert.AreEqual(8, ((byte)7).RoundUpToPowerOf2()); + } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturnSameValue_GivenPowerOf2() + { + for (var i = 0; i < 8; i++) + { + var value = (byte)System.Math.Pow(2, i); + Assert.AreEqual(value, value.RoundUpToPowerOf2()); + } + } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturn0_Given0() + { + Assert.AreEqual(0, ((byte)0).RoundUpToPowerOf2()); + } } diff --git a/X10D.Tests/src/Numerics/Int16Tests.cs b/X10D.Tests/src/Numerics/Int16Tests.cs index 11a4a3d..5a40597 100644 --- a/X10D.Tests/src/Numerics/Int16Tests.cs +++ b/X10D.Tests/src/Numerics/Int16Tests.cs @@ -41,4 +41,29 @@ public class Int16Tests const short value = 2896; // 00001011 01010000 Assert.AreEqual(value, value.RotateRight(16)); } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturnRoundedValue_GivenValue() + { + Assert.AreEqual(4, ((short)3).RoundUpToPowerOf2()); + Assert.AreEqual(8, ((short)5).RoundUpToPowerOf2()); + Assert.AreEqual(8, ((short)6).RoundUpToPowerOf2()); + Assert.AreEqual(8, ((short)7).RoundUpToPowerOf2()); + } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturnSameValue_GivenPowerOf2() + { + for (var i = 0; i < 8; i++) + { + var value = (short) System.Math.Pow(2, i); + Assert.AreEqual(value, value.RoundUpToPowerOf2()); + } + } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturn0_Given0() + { + Assert.AreEqual(0, ((short)0).RoundUpToPowerOf2()); + } } diff --git a/X10D.Tests/src/Numerics/Int32Tests.cs b/X10D.Tests/src/Numerics/Int32Tests.cs index 874b5af..40b8116 100644 --- a/X10D.Tests/src/Numerics/Int32Tests.cs +++ b/X10D.Tests/src/Numerics/Int32Tests.cs @@ -39,4 +39,29 @@ public class Int32Tests const int value = 284719; // 00000000 00000100 01011000 00101111 Assert.AreEqual(value, value.RotateRight(32)); } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturnRoundedValue_GivenValue() + { + Assert.AreEqual(4, 3.RoundUpToPowerOf2()); + Assert.AreEqual(8, 5.RoundUpToPowerOf2()); + Assert.AreEqual(8, 6.RoundUpToPowerOf2()); + Assert.AreEqual(8, 7.RoundUpToPowerOf2()); + } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturnSameValue_GivenPowerOf2() + { + for (var i = 0; i < 8; i++) + { + var value = (int)System.Math.Pow(2, i); + Assert.AreEqual(value, value.RoundUpToPowerOf2()); + } + } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturn0_Given0() + { + Assert.AreEqual(0, 0.RoundUpToPowerOf2()); + } } diff --git a/X10D.Tests/src/Numerics/Int64Tests.cs b/X10D.Tests/src/Numerics/Int64Tests.cs index 2dff9cf..06bfe52 100644 --- a/X10D.Tests/src/Numerics/Int64Tests.cs +++ b/X10D.Tests/src/Numerics/Int64Tests.cs @@ -39,4 +39,29 @@ public class Int64Tests const long value = 5972019251303316844; // 01010010 11100000 11011111 11011110 00110001 10111010 01111101 01101100 Assert.AreEqual(value, value.RotateRight(64)); } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturnRoundedValue_GivenValue() + { + Assert.AreEqual(4L, 3L.RoundUpToPowerOf2()); + Assert.AreEqual(8L, 5L.RoundUpToPowerOf2()); + Assert.AreEqual(8L, 6L.RoundUpToPowerOf2()); + Assert.AreEqual(8L, 7L.RoundUpToPowerOf2()); + } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturnSameValue_GivenPowerOf2() + { + for (var i = 0; i < 8; i++) + { + var value = (long)System.Math.Pow(2, i); + Assert.AreEqual(value, value.RoundUpToPowerOf2()); + } + } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturn0_Given0() + { + Assert.AreEqual(0L, 0L.RoundUpToPowerOf2()); + } } diff --git a/X10D.Tests/src/Numerics/SByteTests.cs b/X10D.Tests/src/Numerics/SByteTests.cs index c24ebcf..dafead3 100644 --- a/X10D.Tests/src/Numerics/SByteTests.cs +++ b/X10D.Tests/src/Numerics/SByteTests.cs @@ -40,4 +40,29 @@ public class SByteTests const sbyte value = 117; // 01110101 Assert.AreEqual(value, value.RotateRight(8)); } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturnRoundedValue_GivenValue() + { + Assert.AreEqual(4, ((sbyte)3).RoundUpToPowerOf2()); + Assert.AreEqual(8, ((sbyte)5).RoundUpToPowerOf2()); + Assert.AreEqual(8, ((sbyte)6).RoundUpToPowerOf2()); + Assert.AreEqual(8, ((sbyte)7).RoundUpToPowerOf2()); + } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturnSameValue_GivenPowerOf2() + { + for (var i = 0; i < 7; i++) + { + var value = (sbyte)System.Math.Pow(2, i); + Assert.AreEqual(value, value.RoundUpToPowerOf2()); + } + } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturn0_Given0() + { + Assert.AreEqual(0, ((sbyte)0).RoundUpToPowerOf2()); + } } diff --git a/X10D.Tests/src/Numerics/UInt16Tests.cs b/X10D.Tests/src/Numerics/UInt16Tests.cs index 23a5d39..27448c2 100644 --- a/X10D.Tests/src/Numerics/UInt16Tests.cs +++ b/X10D.Tests/src/Numerics/UInt16Tests.cs @@ -42,4 +42,29 @@ public class UInt16Tests const ushort value = 2896; // 00001011 01010000 Assert.AreEqual(value, value.RotateRight(16)); } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturnRoundedValue_GivenValue() + { + Assert.AreEqual(4U, ((ushort)3).RoundUpToPowerOf2()); + Assert.AreEqual(8U, ((ushort)5).RoundUpToPowerOf2()); + Assert.AreEqual(8U, ((ushort)6).RoundUpToPowerOf2()); + Assert.AreEqual(8U, ((ushort)7).RoundUpToPowerOf2()); + } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturnSameValue_GivenPowerOf2() + { + for (var i = 0; i < 8; i++) + { + var value = (ushort)System.Math.Pow(2, i); + Assert.AreEqual(value, value.RoundUpToPowerOf2()); + } + } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturn0_Given0() + { + Assert.AreEqual(0U, ((ushort)0).RoundUpToPowerOf2()); + } } diff --git a/X10D.Tests/src/Numerics/UInt32Tests.cs b/X10D.Tests/src/Numerics/UInt32Tests.cs index 5686953..6cc6c94 100644 --- a/X10D.Tests/src/Numerics/UInt32Tests.cs +++ b/X10D.Tests/src/Numerics/UInt32Tests.cs @@ -39,5 +39,28 @@ public class UInt32Tests { const uint value = 284719; // 00000000 00000100 01011000 00101111 Assert.AreEqual(value, value.RotateRight(32)); + } [TestMethod] + public void RoundUpToPowerOf2_ShouldReturnRoundedValue_GivenValue() + { + Assert.AreEqual(4U, 3U.RoundUpToPowerOf2()); + Assert.AreEqual(8U, 5U.RoundUpToPowerOf2()); + Assert.AreEqual(8U, 6U.RoundUpToPowerOf2()); + Assert.AreEqual(8U, 7U.RoundUpToPowerOf2()); + } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturnSameValue_GivenPowerOf2() + { + for (var i = 0; i < 8; i++) + { + var value = (uint)System.Math.Pow(2, i); + Assert.AreEqual(value, value.RoundUpToPowerOf2()); + } + } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturn0_Given0() + { + Assert.AreEqual(0U, 0U.RoundUpToPowerOf2()); } } diff --git a/X10D.Tests/src/Numerics/UInt64Tests.cs b/X10D.Tests/src/Numerics/UInt64Tests.cs index 7d2f026..c2006f9 100644 --- a/X10D.Tests/src/Numerics/UInt64Tests.cs +++ b/X10D.Tests/src/Numerics/UInt64Tests.cs @@ -40,4 +40,29 @@ public class UInt64Tests const ulong value = 5972019251303316844; // 01010010 11100000 11011111 11011110 00110001 10111010 01111101 01101100 Assert.AreEqual(value, value.RotateRight(64)); } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturnRoundedValue_GivenValue() + { + Assert.AreEqual(4UL, 3UL.RoundUpToPowerOf2()); + Assert.AreEqual(8UL, 5UL.RoundUpToPowerOf2()); + Assert.AreEqual(8UL, 6UL.RoundUpToPowerOf2()); + Assert.AreEqual(8UL, 7UL.RoundUpToPowerOf2()); + } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturnSameValue_GivenPowerOf2() + { + for (var i = 0; i < 8; i++) + { + var value = (ulong)System.Math.Pow(2, i); + Assert.AreEqual(value, value.RoundUpToPowerOf2()); + } + } + + [TestMethod] + public void RoundUpToPowerOf2_ShouldReturn0_Given0() + { + Assert.AreEqual(0UL, 0UL.RoundUpToPowerOf2()); + } } diff --git a/X10D/src/Numerics/ByteExtensions.cs b/X10D/src/Numerics/ByteExtensions.cs index 02c6e74..6435be4 100644 --- a/X10D/src/Numerics/ByteExtensions.cs +++ b/X10D/src/Numerics/ByteExtensions.cs @@ -48,4 +48,23 @@ public static class ByteExtensions count = count.Mod(8); return (byte)((value >> count) | (value << (8 - count))); } + + /// + /// Rounds the current value up to a power of two. + /// + /// The value to round. + /// + /// The smallest power of two that's greater than or equal to , or 0 if + /// is 0 or the result overflows. + /// + [Pure] +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#endif + public static byte RoundUpToPowerOf2(this byte value) + { + return (byte)((uint)value).RoundUpToPowerOf2(); + } } diff --git a/X10D/src/Numerics/Int16Extensions.cs b/X10D/src/Numerics/Int16Extensions.cs index 1a3656b..db48f91 100644 --- a/X10D/src/Numerics/Int16Extensions.cs +++ b/X10D/src/Numerics/Int16Extensions.cs @@ -47,4 +47,23 @@ public static class Int16Extensions var unsigned = unchecked((ushort)value); return unchecked((short)unsigned.RotateRight(count)); } + + /// + /// Rounds the current value up to a power of two. + /// + /// The value to round. + /// + /// The smallest power of two that's greater than or equal to , or 0 if + /// is 0 or the result overflows. + /// + [Pure] +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#endif + public static short RoundUpToPowerOf2(this short value) + { + return (short)((uint)value).RoundUpToPowerOf2(); + } } diff --git a/X10D/src/Numerics/Int32Extensions.cs b/X10D/src/Numerics/Int32Extensions.cs index 3bf74b9..74ebca4 100644 --- a/X10D/src/Numerics/Int32Extensions.cs +++ b/X10D/src/Numerics/Int32Extensions.cs @@ -47,4 +47,23 @@ public static class Int32Extensions var unsigned = unchecked((uint)value); return unchecked((int)unsigned.RotateRight(count)); } + + /// + /// Rounds the current value up to a power of two. + /// + /// The value to round. + /// + /// The smallest power of two that's greater than or equal to , or 0 if + /// is 0 or the result overflows. + /// + [Pure] +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#endif + public static int RoundUpToPowerOf2(this int value) + { + return (int)((uint)value).RoundUpToPowerOf2(); + } } diff --git a/X10D/src/Numerics/Int64Extensions.cs b/X10D/src/Numerics/Int64Extensions.cs index 695ed4b..f3a3283 100644 --- a/X10D/src/Numerics/Int64Extensions.cs +++ b/X10D/src/Numerics/Int64Extensions.cs @@ -47,4 +47,23 @@ public static class Int64Extensions var unsigned = unchecked((ulong)value); return unchecked((long)unsigned.RotateRight(count)); } + + /// + /// Rounds the current value up to a power of two. + /// + /// The value to round. + /// + /// The smallest power of two that's greater than or equal to , or 0 if + /// is 0 or the result overflows. + /// + [Pure] +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#endif + public static long RoundUpToPowerOf2(this long value) + { + return (long)((ulong)value).RoundUpToPowerOf2(); + } } diff --git a/X10D/src/Numerics/SByteExtensions.cs b/X10D/src/Numerics/SByteExtensions.cs index eb6ce07..85c393d 100644 --- a/X10D/src/Numerics/SByteExtensions.cs +++ b/X10D/src/Numerics/SByteExtensions.cs @@ -48,4 +48,23 @@ public static class SByteExtensions var signed = unchecked((byte)value); return unchecked((sbyte)signed.RotateRight(count)); } + + /// + /// Rounds the current value up to a power of two. + /// + /// The value to round. + /// + /// The smallest power of two that's greater than or equal to , or 0 if + /// is 0 or the result overflows. + /// + [Pure] +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#endif + public static sbyte RoundUpToPowerOf2(this sbyte value) + { + return (sbyte)((uint)value).RoundUpToPowerOf2(); + } } diff --git a/X10D/src/Numerics/UInt16Extensions.cs b/X10D/src/Numerics/UInt16Extensions.cs index 54403c9..af4e344 100644 --- a/X10D/src/Numerics/UInt16Extensions.cs +++ b/X10D/src/Numerics/UInt16Extensions.cs @@ -46,4 +46,23 @@ public static class UInt16Extensions { return (ushort)((ushort)(value >> count) | (ushort)(value << (16 - count))); } + + /// + /// Rounds the current value up to a power of two. + /// + /// The value to round. + /// + /// The smallest power of two that's greater than or equal to , or 0 if + /// is 0 or the result overflows. + /// + [Pure] +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#endif + public static ushort RoundUpToPowerOf2(this ushort value) + { + return (ushort)((uint)value).RoundUpToPowerOf2(); + } } diff --git a/X10D/src/Numerics/UInt32Extensions.cs b/X10D/src/Numerics/UInt32Extensions.cs index 7381b1b..51940af 100644 --- a/X10D/src/Numerics/UInt32Extensions.cs +++ b/X10D/src/Numerics/UInt32Extensions.cs @@ -1,4 +1,5 @@ using System.Diagnostics.Contracts; +using System.Numerics; using System.Runtime.CompilerServices; namespace X10D.Numerics; @@ -46,4 +47,34 @@ public static class UInt32Extensions { return (value >> count) | (value << (32 - count)); } + + /// + /// Rounds the current value up to a power of two. + /// + /// The value to round. + /// + /// The smallest power of two that's greater than or equal to , or 0 if + /// is 0 or the result overflows. + /// + [Pure] +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#endif + public static uint RoundUpToPowerOf2(this uint value) + { +#if NET6_0_OR_GREATER + return BitOperations.RoundUpToPowerOf2(value); +#else + // Based on https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + --value; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + return value + 1; +#endif + } } diff --git a/X10D/src/Numerics/UInt64Extensions.cs b/X10D/src/Numerics/UInt64Extensions.cs index 0f10aba..d97c1f7 100644 --- a/X10D/src/Numerics/UInt64Extensions.cs +++ b/X10D/src/Numerics/UInt64Extensions.cs @@ -1,4 +1,5 @@ using System.Diagnostics.Contracts; +using System.Numerics; using System.Runtime.CompilerServices; namespace X10D.Numerics; @@ -46,4 +47,35 @@ public static class UInt64Extensions { return (value >> count) | (value << (64 - count)); } + + /// + /// Rounds the current value up to a power of two. + /// + /// The value to round. + /// + /// The smallest power of two that's greater than or equal to , or 0 if + /// is 0 or the result overflows. + /// + [Pure] +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#endif + public static ulong RoundUpToPowerOf2(this ulong value) + { +#if NET6_0_OR_GREATER + return BitOperations.RoundUpToPowerOf2(value); +#else + // Based on https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + --value; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value |= value >> 32; + return value + 1; +#endif + } }