【问题标题】:How to check if value will fit into type?如何检查值是否适合类型?
【发布时间】:2014-03-03 21:33:51
【问题描述】:

假设我有一个名为 T 的类型名称,它的大小始终为 >= T2 两者都是无符号的。如何检查T 类型的变量a 是否适合T2 而不会溢出,如果它不适合做一些其他操作?我试过检查它是否变成负数,但我不确定这是否是正确的检查方法,如下所示:

T a = ...;
T2 b = a;
if(b < 0) // didn't fit
else // ok, fit

【问题讨论】:

标签: c types overflow


【解决方案1】:

这不起作用:如果 TT2 未签名,b &lt; 0 将始终为 false。

由于这两种类型都是无符号的,因此可以保证溢出会导致回绕(旁注:如果这些是有符号类型,溢出会导致 UB,尽管它通常也会回绕)。因此,您可以使用类似:

T a = ...;
T2 b = a;
if ((T) b != a) {
   /* didn't fit */
}
else { ... }

围绕b 的强制转换不是绝对必要的:如果sizeof(T) > sizeof(T2),那么通常的算术转换会导致b 在与a 比较之前转换为T。但是,为了清楚起见,我选择明确地将其留在那里。

根据 C99 的第 6.3.1.3 节(参见下面的 cmets),当无符号整数转换为更窄的无符号类型时会发生这种情况:

如果新类型是无符号的,则该值被反复转换 比最大值多加或减一 以新类型表示,直到值在新类型的范围内 输入

这意味着在溢出的情况下,b 在转换回T 时将不等于a。这就是此代码背后的基本原理。此外,它会少于a,因此您可以根据需要将!= 切换为&lt;

或者,您可以在分配给b 之前检查以确保它适合:

T a = ...;
T2 b;

if (a > (T2)-1) {
    /* won't fit */
}
else {
    /* fits */
}

(T2) -1 是无符号类型T2 可以容纳的最大值。如果a 大于这个值,那么它显然不适合b。要了解为什么 (T2) -1 是可移植的并且始终有效,请参阅以下问题:Is it safe to use -1 to set all bits to true?

【讨论】:

  • 我知道在实践中(T2)~0ULL 是无符号类型T2 可以容纳的最大值,但是......这是由标准保证的吗?只是扮演魔鬼的拥护者
  • 你为什么说b周围的演员是必要的? AFAIK,不是,根据 6.3.1.8 通常的算术转换
  • 我会尝试回答我的第一个问题:根据6.3.1.3 有符号和无符号整数如果新类型为无符号,则值转换为在新类型中可以表示的最大值重复加或减一,直到该值在新类型的范围内,这保证(T2)~0ULL是无符号类型T2可以容纳的最大值
  • 关于您的第二个问题:强制转换实际上是不必要的,因为通常的算术转换会将b 转换为T,如果实际上是sizeof(T) &gt; sizeof(T2)
  • @WalterTross 关于您的第一个问题:您是对的。我什至没有考虑这个规则,我基本上假设演员会将~0ULL缩小到T的大小。我更新了我的答案。
【解决方案2】:

因为T2 未签名,您的if (b &lt; 0)工作。以下应该没问题:

    T a = ...;
    T2 b;
    if (a > (T2)-1) // a won't fit into a T2
       //...
    else
       b = a; // safe, no overflow, value unchanged

您需要-1 而不是~0,因为后者是int,如果T2int 更宽,则将不起作用。 (但~0LL 将适用于所有常用类型。)

我相信对于一个无符号类型T2,它的最大值总是(T2)-1。来自 C99 标准:

6.3.1.3 有符号和无符号整数

1 当一个整数类型的值被转换为另一个整数类型而不是_Bool时,如果 该值可以用新类型表示,它是不变的。

2 否则,如果新类型是无符号的,则通过重复添加或转换值 比新类型可以表示的最大值减一 直到值在新类型的范围内。49)

3 否则,新类型是有符号的,值不能在其中表示;无论是 结果是实现定义的或引发了实现定义的信号。

因此,例如,如果T2 的最大值为10,则通过将11 添加到-1 来评估(T2)-1,得到10,根据需要。

另请参阅Question about C behaviour for unsigned integer underflowIs it safe to assign -1 to an unsigned int to get the max value?Signed to unsigned conversion in C - is it always safe?

【讨论】:

【解决方案3】:
T  a = ...;
T2 b = a;
if (b < a) // didn't fit
else // ok, fit

距离你写的内容只有一个字节:-)

这是可行的,因为对于无符号类型,溢出总是会生成一个较小的值,并且比较只会将 b 转换为 T,这是没有问题的。

来自 C99 标准(6.3.1.3 有符号和无符号整数):

[...] 如果新类型是无符号的,则值为 通过重复加或减一比转换 可以在新类型中表示的最大值,直到该值 在新类型的范围内。

这也意味着 Joseph Quinsey 的解决方案是正确的:您始终可以将 a(T2)-1 进行比较,这可以保证是无符号类型 T2 可以容纳的最大值:

if (a > (T2)-1)) // won't fit

请注意,标准从不提及“尺寸”(除非提及sizeof),以保持通用性。当然,我引用的迭代过程归结为可能在所有当代架构中截断高位,但你永远不知道......

【讨论】:

  • 只有当 T 和 T2 都是有符号或无符号时才能保证工作 这是不正确的。根据 C 标准规则,签名类型溢出会导致 UB。
  • 对不起@FilipeGonçalves,在你发表评论之前,我已经完全改写了我的回答——无论如何,你是对的
【解决方案4】:

我在这里放置了一些伪代码来提供帮助。通过整理,你应该可以从那个伪代码中得到答案。

您要做的是检查所有位 > T2 中的位数。如果其中任何一个为“1”,则您无法将该特定 T 变量安全地存储在 T2 中。

// If T is an int (32 bits) and T2 is a char (8 bits):
// assume variable a is the integer in question.

int mask = 0;
for (int i = sizeof(T2); i < sizeof(T); ++i)
{
    int mask = 1 << i;
    if (a & mask)
    {
        // if this hits, we have a "1" in that bit, so the value cannot fit.
        return false;
    }
    return true;
}

【讨论】:

    猜你喜欢
    • 2021-10-07
    • 1970-01-01
    • 2011-05-07
    • 2016-05-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多