【问题标题】:Using builtin overflow functions with uint64_t types使用带有 uint64_t 类型的内置溢出函数
【发布时间】:2019-12-17 11:48:20
【问题描述】:

我目前正在编写一个大量使用uint64_t 类型的程序,以增强跨平台的兼容性。 碰巧,我的程序有很多溢出的可能性,所以我想使用built in overflow functions from gcc

不幸的是,它们仅在ints、long ints、long long ints 等上定义。 uintX_t 类型保证始终大小为 X 位,但对于这些类型而言并非如此,例如long int 不保证为 64 位。这让我觉得,这里不能使用内置的溢出函数。

现在如何解决这个问题?

我有两种方法:

  1. 使用来自stdint.hUINT64_MAX 常量并自己进行溢出预测。但是,我不是“重新发明轮子”的朋友。

  2. 使用例如__builtin_add_overflow_p 函数仅检查溢出。但是,我不能 100% 确定它们是否可以应用于 uint64_t

最好的方法是什么?我在监督一些明显的事情吗?

【问题讨论】:

  • 可能有一个函数为“这个溢出”返回 bool 可能很有用。当然,使用一些 GCC 特定的东西对于兼容性并不是很好。就x86 而言,如果我记得出于 unsigned 的目的,可以使用“进位标志”,但我不确定 GCC 是否会公开这一点。
  • 我会尝试__builtin_add_overflow 并让编译器整理出类型。 uint64_t 存在但与 unsigned longunsigned long long 不匹配的可能性极小。除非您想移植到曾经制造的每个系统,否则我认为它与现代实现已经足够接近了。如果您需要额外检查,只需添加静态断言。

标签: c gcc built-in integer-overflow


【解决方案1】:

通过uint64_t使用内置溢出函数
...使用built in overflow functions from gcc

要形成自己的builtin_uadd64_overflow(),无需“重新发明轮子”,请使用_Generic 来引导功能选择。

#define builtin_uadd64_overflow(a,b,r) _Generic(*(r), \
  unsigned: __builtin_uadd_overflow, \
  unsigned long: __builtin_uaddl_overflow, \
  unsigned long long: __builtin_uaddll_overflow \
  )(a,b,r)

【讨论】:

  • 嗯,Link 有“第一个内置函数允许操作数的任意整数类型,并且结果类型必须是指向除枚举或布尔类型之外的某个整数类型的指针,其余的内置函数函数具有明确的整数类型。”,所以@user694733 的想法看起来更好。
【解决方案2】:

无符号溢出是根据 C 标准 (§6.2.5/9) 定义的行为

涉及无符号操作数的计算永远不会溢出,因为无法由生成的无符号整数类型表示的结果会以比结果类型可以表示的最大值大一的数字为模减少。

所以当你有一个像这样的加法时;

uint64_t a, b, c;

c = a + b;

您可以通过测试检查操作是否溢出

int overflowed = c < a || c < b;

当然,如果需要,您可以将其包装成一个函数。

【讨论】:

  • int overflowed = c &lt; a; 还不够吗?
  • @chux-ReinstateMonica 是的,你是对的,只是添加了另一个选项来说明对称性。两者之一可以省略。
【解决方案3】:

以下函数对于intlong long 类型的对象应该工作得很好;在 32 位平台上,int 函数将比使用 long long 的方法更有效;在 64 位平台上,long long 函数将与 int 函数一样高效。

int safe_add1(int  *value, int  delta) {
    int val = *value;
    int sum = delta+(unsigned int)val;
    if (((sum ^ val) & (sum ^ delta)) < 0)
        return -1;
    *value = sum;
    return 0;
}
int safe_add2(long long  *value, long long  delta) {
    long long val = *value;
    long long sum = delta+(unsigned long long)val;
    if (((sum ^ val) & (sum ^ delta)) < 0)
        return -1;
    *value = sum;
    return 0;
}

请注意,要真正有效地防止溢出,在许多情况下,编译器需要使用整数溢出作为邀请来实现符合标准的扩展,该扩展将定义整数溢出语义的方式足够严格以满足程序要求,但松散足以允许优化。例如,如果一个程序想要计算 x+y &gt; z,同时确保不会检测到会产生 numerically-wrong 行为的溢出,并且如果编译器知道 xz 将相等,满足要求的最佳方法是计算y &gt; 0,因为无论x+y 是否可以在没有溢出的情况下计算,该行为在数值上都是正确的

不幸的是,除非编译器提供扩展来识别此类结构,否则将无法编写特别有效的溢出检查代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-21
    • 2011-06-04
    • 1970-01-01
    • 2018-11-03
    • 2022-11-17
    • 1970-01-01
    • 2021-04-03
    • 1970-01-01
    相关资源
    最近更新 更多