How can I determine the number of bits needed for a generic integer data type in C#?

huangapple go评论71阅读模式
英文:

How can I determine the number of bits needed for a generic integer data type in C#?

问题

Here's the translated content:

我想确定在方法内部需要多少位来存储通用整数二进制数据类型(.NET 7的一个新功能 — 请参阅IBinaryInteger<T>)。我不想使用 "sizeof" 因为它需要不安全编译。我想出了一个解决方案(实际上有几种),但很难相信没有更好的答案。

以下是一个解决方案,以及一个测试程序:

using System.Numerics;

namespace BitCountApp {

    internal class Program {

        static void Main(string[] args) {
            Console.WriteLine("Bits in byte = " + CountBits<byte>());
            Console.WriteLine("Bits in ushort = " + CountBits<ushort>());
            Console.WriteLine("Bits in uint = " + CountBits<uint>());
            Console.WriteLine("Bits in ulong = " + CountBits<ulong>());
            Console.WriteLine("Bits in UInt128 = " + CountBits<UInt128>());
        }

        static private int CountBits<T>() where T : IBinaryInteger<T> {
            return T.AllBitsSet.GetByteCount() * 8;
        }

    }

}

这个解决方案有效,而且非常简洁,但仅适用于大小是8的倍数的数据。这可能更多是理论上的问题,但对于实现了IBinaryInteger的7位无符号整数的 "UInt7" 数据类型来说,它将失败。

"啊!PopCount!"我听到你的呼声。但是有一个问题。如果我尝试使用 PopCount 来实现 CountBits ...

static private int BitsForType<T>() where T : IBinaryInteger<T> {
    return (int)(T.PopCount(T.AllBitsSet));
}

...我发现无法将 PopCount 的值(返回 "T")强制转换为 "int"(即使 T 是二进制整数类型)。

有一种可以正确工作的解决方案 — 自己计算位数,就像这样 ...

static private int CountBits<T>() where T : IBinaryInteger<T> {
    var ones = T.AllBitsSet;
    int count = 0;
    while (ones != T.Zero) {
        count++;
        ones >>= 1;
    };
    return count;
}

... 但是,呃!

有没有一种方法可以在不自己计算位数的情况下做到这一点?我有点震惊没有内置的属性或方法可以实现这一点。

英文:

I want to determine the number of bits needed for a generic integral binary data type (a nice new feature of .NET 7 -- see IBinaryInteger&lt;T&gt;) inside a method. I don't want to use "sizeof" because it requires unsafe compilation. I've come up with a solution (actually several), but have a tough time believing there's not a better answer.

Here's one solution, along with a test program:

using System.Numerics;

namespace BitCountApp {

    internal class Program {

        static void Main(string[] args) {
            Console.WriteLine(&quot;Bits in byte = &quot; + CountBits&lt;byte&gt;());
            Console.WriteLine(&quot;Bits in ushort = &quot; + CountBits&lt;ushort&gt;());
            Console.WriteLine(&quot;Bits in uint = &quot; + CountBits&lt;uint&gt;());
            Console.WriteLine(&quot;Bits in ulong = &quot; + CountBits&lt;ulong&gt;());
            Console.WriteLine(&quot;Bits in UInt128 = &quot; + CountBits&lt;UInt128&gt;());
        }

        static private int CountBits&lt;T&gt;() where T : IBinaryInteger&lt;T&gt; {
            return T.AllBitsSet.GetByteCount() * 8;
        }

    }

}

This solution works, and it's nice and compact, but it works only for data sizes that are multiples of 8. This may be more theoretical than practical, but it would fail for a "UInt7" datatype that implemented IBinaryInteger with a 7-bit unsigned integer.

"Ah! PopCount!" I hear you cry. Well, there's a problem. If I try to implement CountBits using PopCount ...

static private int BitsForType&lt;T&gt;() where T : IBinaryInteger&lt;T&gt; {
    return (int)(T.PopCount(T.AllBitsSet));
}

... I find that I cannot cast the value of PopCount, which returns a "T", to "int" (even though T is a binary integer type).

There is a solution that works correctly -- counting the number of bits for myself, like this ...

static private int CountBits&lt;T&gt;() where T : IBinaryInteger&lt;T&gt; {
    var ones = T.AllBitsSet;
    int count = 0;
    while (ones != T.Zero) {
        count++;
        ones &gt;&gt;= 1;
    };
    return count;
}

... but, Yuk!

Is there a way to do this without counting the bits myself? I'm sort of shocked there isn't a built-in property or method for this.

答案1

得分: 0

以下是翻译好的内容:

这里有一个在 GitHub 上由 @huoyaoyuan 提供的很好的答案,使用了 CreateChecked 方法(感谢 @Ian 的建议):

> 使用 int.CreateXXX 进行转换。

所以我会有:

static private int CountBits&lt;T&gt;() where T : IBinaryInteger&lt;T&gt; {
    return int.CreateChecked(T.PopCount(T.AllBitsSet));
}

还有一些稍有不同行为的方法,如 CreateSaturatingCreateTruncating

英文:

Here's a great answer provided by @huoyaoyuan on GitHub that uses the CreateChecked method (thanks for the suggestion @Ian):

> Use int.CreateXXX to cast.

So I would have:

static private int CountBits&lt;T&gt;() where T : IBinaryInteger&lt;T&gt; {
    return int.CreateChecked(T.PopCount(T.AllBitsSet));
}

There are also CreateSaturating and CreateTruncating methods that behave slightly differently.

huangapple
  • 本文由 发表于 2023年5月21日 05:29:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/76297426.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定