【问题标题】:The max number of digits in an int based on number of bits基于位数的 int 中的最大位数
【发布时间】:2017-05-04 15:55:13
【问题描述】:

因此,我需要一个常量值来表示 int 中的最大位数,并且需要在编译时计算它以传递到 char 数组的大小。

添加更多细节:我正在使用的编译器/机器具有非常有限的 C 语言子集,因此所有标准库都无法工作,因为它们具有不受支持的功能。因此我不能使用 INT_MIN/MAX,因为我既不能包含它们,也不能定义它们。

我需要一个计算大小的编译时表达式。我想出的公式是:

((sizeof(int) / 2) * 3 + sizeof(int)) + 2

基于手工计算的 n 字节整数略微成功。

sizeof(int)  INT_MAX               characters    formula
2           32767                       5            7
4           2147483647                  10           12
8           9223372036854775807         19           22

【问题讨论】:

  • 你的意思是十进制位的最大个数吗?
  • @JohnBollinger:这是一个很好的观点。我假设十进制数字。如果 OP 想要二进制数字,那么请对我的答案投反对票。
  • 感谢大家的回答,我应该更明确一点,我正在使用的编译器/机器不能包含任何标准库,因为它们具有不受支持的功能。因此,我也无权访问 MAX_INT 或 MIN_INT。
  • @BenSeawalker,如果您阅读了 chux 的答案和我的答案,您会发现虽然他们用MAX_INTMIN_INT 描述了解决方案,但最终它们并不依赖于这些宏。 chux 确实依赖于CHAR_BIT,但用文字 8 替换它的风险很小。
  • 话虽如此,每个符合标准的 C 实现,包括听起来像您正在使用的独立实现,都支持使用由标准库头文件的指定子集定义的功能。该子集包括limits.h,其中定义了INT_MAXINT_MINCHAR_BIT 宏。如果您不能使用该标头,那么您的实现是不合格的。实际上,它不是 C。

标签: c


【解决方案1】:

您正在寻找与相关整数类型最大值的对数相关的结果(哪个对数取决于您要计算其位数的表示的基数)。您无法在编译时计算精确的对数,但您可以编写宏来为您的目的足够接近地估计它们,或者为您的目的计算足够接近的上限。例如,请参阅How to compute log with the preprocessor

知道您可以通过乘以适当的常数在不同底数的对数之间进行转换也很有用。特别是,如果您知道一个数字的底-a 对数并且想要底-b 对数,则可以将其计算为

logb(x) = loga(x) / loga子>(b)

不过,您的情况比一般情况要容易一些。对于不是变长数组的数组的维度,你需要一个“整数常量表达式”。此外,对于您在 C 实现中找到的任何内置整数类型,您的结果不需要超过两位数的精度(如果您想要 binary 位数,则为三位),而且看起来就像你只需要一个足够接近的上限。

此外,您还可以从 sizeof 运算符开始,它可以出现在整数常量表达式中,当应用于整数类型时,它会为您提供该类型值的以 256 为底的对数的上限(假设CHAR_BIT 是 8)。如果每个位都是一个值位,这个估计是非常严格的,但是有符号整数有一个符号位,它们也可能有填充位,所以这个界限对他们来说有点松散。

如果您想要对 2 的幂基数中的位数进行限制,那么您可以直接使用 sizeof。不过,让我们假设您正在查找 decimal 位数。在数学上,int 十进制表示的最大位数是

N = ceil(log10(MAX_INT))

N = floor(log10(MAX_INT)) + 1

假设 MAX_INT 不是 10 的幂。让我们用以 256 为底的对数来表示:

N = floor(log256(MAX_INT) / log256(10) ) + 1

现在,log256(10) 不能是整数常量表达式的一部分,但它或其倒数 可以预先计算:1 / log 256(10) = 2.40824(非常接近;实际值略小)。现在,让我们用它来重写我们的表达式:

N

这还不是一个整数常量表达式,但它很接近。这个表达式一个整数常量表达式,并且是一个足够好的近似值来满足你的目的:

N = 241 * sizeof(int) / 100 + 1

以下是各种整数大小的结果:

sizeof(int) INT_MAX True N 计算 N 1 127 3 3 2 32767 5 5 4 2147483648 10 10 8 ~9.223372037e+18 19 20

INT_MAXTrue N 列中的值假设有符号表示的一种允许形式,并且没有填充位;如果表示包含填充位,则前者和可能两者都会更小。)

我认为,万一您遇到具有 8 字节 ints 的系统,您为数字数组提供的额外一个字节不会破坏您。差异源于有符号的 64 位整数中(最多)63 个值位与在这种情况下占 64 个值位的公式之间的差异,结果sizeof(int) 有点高估了INT_MAX 的 base-256 日志。如果没有填充位,该公式给出了 unsigned int 的精确结果,至少大小为 8。

作为一个宏,那么:

// Expands to an integer constant expression evaluating to a close upper bound
// on the number the number of decimal digits in a value expressible in the
// integer type given by the argument (if it is a type name) or the the integer
// type of the argument (if it is an expression). The meaning of the resulting
// expression is unspecified for other arguments.
#define DECIMAL_DIGITS_BOUND(t) (241 * sizeof(t) / 100 + 1)

【讨论】:

  • 虽然这个答案非常全面,但我认为它绕过了(在某些地方)一些棘手的角落:int 可能有填充位。您拥有的 sizeof 表是迂腐的,具有误导性。甚至 IEEE754 都没有对log 的准确性提出要求,所以你也需要小心。我仍然坚信,您可以解决此问题的唯一可移植方法是仅根据 INT_MAX 的值定义常量。
  • 感谢紫外线,@Bathsheba。我同意int 表示可以有填充位,并且该表描述了没有填充位的情况,但答案只需要并旨在提供最大小数位数的 上限位数。我将澄清这就是它提供的。但是,库的log() 函数的准确性无关紧要,因为这里没有使用它。我同意,如果您希望在存在填充位的情况下获得尽可能严格的界限,那么您需要使用基于 INT_MAX 的东西,但 OP 在 cmets 中说不能使用 INT_MAX
  • 注意:241 * sizeof(t) / 100 假设CHAR_BIT==8 - 这是非常常见的,但未指定。
  • 是的,@chux,虽然在这个冗长的答案中可能很难发现,但我确实指出这是一个附带条件(第四段中间)。
【解决方案2】:

int 可能产生的十进制位数的上限取决于INT_MIN

// Mathematically 
max_digits = ceil(log10(-INT_MAX))

使用int 的位宽更容易,因为它近似于-INT_MIN 的日志。 sizeof(int)*CHAR_BIT - 1int值位的最大数量。

// Mathematically 
max_digits = ceil((sizeof(int)*CHAR_BIT - 1)* log10(2))
// log10(2) --> ~ 0.30103

在稀有机器上,int 有填充,所以上面的估计会高估。

对于 log10(2),大约为 0.30103,我们可以使用 1/3 或三分之一。

作为一个宏,执行整数运算并为 ceiling

加 1
#include <stdlib.h>
#define INT_DIGIT10_WIDTH ((sizeof(int)*CHAR_BIT - 1)/3 + 1)

要考虑符号和空字符加 2,请使用以下内容。使用非常严格的 log10(2) 分数,不会过度计算缓冲区需求:

#define INT_STRING_SIZE ((sizeof(int)*CHAR_BIT - 1)*28/93 + 3)

注 28/93 = 0.3010752...> log2(10)


任何底数到底数 2 所需的位数如下所示。有趣的是需要 +2 而不是 +1。考虑以 2 为底的 2 位有符号数可能是 "-10",大小为 4。

#define INT_STRING2_SIZE ((sizeof(int)*CHAR_BIT + 2)

【讨论】:

    【解决方案3】:

    无聊的是,我认为您需要对此进行硬编码,重点是检查 sizeof(int) 并查阅您的编译器文档以查看您实际拥有的 int 类型。 (所有C标准规定是不能小于short,并且需要有至少-32767到+32767的范围,并且可以选择1的补码、2的补码和有符号的幅度。尽管大端和小端很常见,但存储方式是任意的。)请注意,允许使用任意数量的填充位,因此您不能完全概括地从sizeof.

    C 不支持您为此需要的编译时可评估常量表达式级别。

    所以硬编码它,故意让你的代码变脆,这样如果编译器遇到你没有想到的情况,编译就会失败。

    您可以在 C++ 中使用 constexpr 和元编程技术解决此问题。

    【讨论】:

    • C++ 可能开箱即用:constexpr const int digits = std::numeric_limits&lt;int&gt;::digits10;但是,不确定它的便携性如何。
    • 哈! C++ 标准委员会接下来会怎么想?
    【解决方案4】:

    如果我们假设整数值是 2、4 或 8 字节,并且如果我们确定相应的数字是 5、10、20,那么产生精确值的整数常量表达式可以写成如下:

    const int digits = (sizeof(int)==8) ? 20 : ((sizeof(int)==4) ? 10 : 5);
    int testArray[digits];
    

    我希望我没有错过一些重要的东西。我已经在文件范围内对此进行了测试。

    【讨论】:

    • 该帖子要求“基于位数的 int 中的最大位数”。对于 64 位 int,这将是 19,而不是 20,因为 INT_MAX 在这种情况下是 9,223,372,036,854,775,808 - 一个 19 位数字。 20 对于unsigned 是有意义的,但这不是问题。
    • @chux:对; std::numeric_limits&lt;int&gt;::digits10 也给19;但我决定超越 OP 的手工计算值。
    【解决方案5】:
    ((sizeof(int) / 2) * 3 + sizeof(int)) + 2
    

    是我想出的公式。 +2 用于负号和空终止符。

    【讨论】:

    • 实际上,我收回了我的反对票。尽管这很粗糙,但在数量惊人的大量情况下都能完成这项工作。如果你用编译器和特定于平台的#defines 包围它,它也接近健壮性。
    猜你喜欢
    • 2021-04-28
    • 1970-01-01
    • 2012-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多