所以我有这个代码:
uint32_t s1 = 0xFFFFFFFFU;
uint32_t s2 = 0xFFFFFFFFU;
uint32_t v;
...
v = s1 * s2; /* Only need the low 32 bits of the result */
在以下所有内容中,我假设编译器对s1
或s2
的范围没有任何先入之见,初始化程序仅用于上述示例。
如果我在一个整数大小为32位的编译器上编译它(例如编译x86时),没问题。编译器只会使用s1
和s2
作为uint32_t
类型的值(无法进一步提升它们),而乘法只会给出结果,如注释所示(modulo {{在这种情况下,这是0x100000000。)
但是,如果我在具有64位整数大小的编译器(例如x86-64)上编译它,则可能会有从C标准中推断出的未定义行为。整数提升会看到UINT_MAX + 1
可以提升为uint32_t
(64位签名),然后乘法会尝试乘以两个int
,如果它们碰巧有示例中显示的值将导致整数溢出,这是未定义的行为。
我对此是否正确,如果是这样,你会如何以理智的方式避免它?
我发现这个问题类似,但涵盖了C ++:What's the best C++ way to multiply unsigned integers modularly safely?。在这里,我想得到一个适用于C的答案(最好是C89兼容)。我不会考虑让一台糟糕的32位机器潜在地执行64位乘法,这是一个可接受的答案(通常在代码中,这会引起关注,32位性能可能更为关键,因为通常那些机器速度较慢)。
请注意,当使用具有32位int大小的编译器编译时,同样的问题可以应用于16位无符号整数;当使用具有16位int大小的编译器编译时,同样的问题可以应用于无符号字符(后者可能对于编译器而言是常见的8位CPU:C标准要求整数至少为16位,因此符合标准的编译器可能会受到影响。)
答案 0 :(得分:27)
在至少为uint32_t
且至少为unsigned int
的无符号类型中实现乘法的最简单方法是使用类型为unsigned int
的表达式。< / p>
v = 1U * s1 * s2;
这会将1U
转换为uint32_t
,或将s1
和s2
转换为unsigned int
,具体取决于适合您特定平台的内容。
@Deduplicator评论说,uint32_t
比unsigned int
窄的某些编译器可能会警告分配中的隐式转换,并注意到通过使转换显式可能会抑制此类警告: / p>
v = (uint32_t) (1U * s1 * S2);
在我看来,它看起来有点不那么优雅了。
答案 1 :(得分:10)
祝贺找到摩擦点。
一种可能的方式:
v = (uint32_t) (UINT_MAX<=0xffffffff
? s1 * s2
: (unsigned)s1 * (unsigned)s2);
无论如何,看起来像<stdint.h>
添加了一些typedef,保证不会小于int
的类型; - )。