32位无符号乘法64位导致未定义的行为?

所以我有这个代码:

uint32_t s1 = 0xFFFFFFFFU; uint32_t s2 = 0xFFFFFFFFU; uint32_t v; ... v = s1 * s2; /* Only need the low 32 bits of the result */ 

在所有的下面,我假设编译器不能对s1s2的范围有任何的先入之见,初始值设定项仅用于上面的一个例子。

如果我在一个整数大小为32位的编译器(例如编译x86时)上编译这个,没问题。 编译器会简单地使用s1s2作为uint32_ttypes的值(不能进一步提升它们),乘法只会给出结果,如注释所示(模UINT_MAX + 1 ,这是0x100000000这种情况)。

但是,如果我编译这个整数大小为64位的编译器(如x86-64),可能会有一些未定义的行为,我可以从C标准中推断出来。 整数提升会看到uint32_t可以被提升为int (64位有符号),然后乘法会试图乘以两个int ,如果碰巧有例子中显示的值,会导致整数溢出,这就是未定义的行为。

我纠正这一点,如果是的话,你会如何避免它以一种理智的方式?

我发现了这个类似的问题,但涵盖了C ++: 安全地模块化乘无符号整数的最佳C ++方法是什么? 。 在这里我想得到一个适用于C的答案(最好是C89兼容)。 尽pipe(通常在代码中,这是值得关注的,32位性能可能更为重要,因为通常那些是较慢的机器),所以我不会考虑制作一台可怜的32位机器,可能会执行一个64位乘法的可接受答案。

请注意,使用32位int大小的编译器编译时,同样的问题可以应用于16位无符号整数,编译时使用16位int大小的编译器编译无符号字符(后者可能与8位CPU的编译器相同:C标准要求整数至less为16位,所以符合的编译器可能会受到影响)。

获得至less为uint32_t的无符号types的乘法以及至lessunsigned int的最简单的方法是涉及一个unsigned inttypes的expression式。

 v = 1U * s1 * s2; 

这要么将1U转换为uint32_t ,要么将s1s2unsigned int ,具体取决于适合您特定平台的内容。

@Deduplicator评论说,一些编译器,其中uint32_tunsigned int窄,可能会警告赋值中的隐式转换,并指出这样的警告可能通过明确地进行转换而被抑制:

 v = (uint32_t) (1U * s1 * S2); 

在我看来,它看起来不那么优雅。

恭喜find摩擦点。

一个可能的方法:

 v = (uint32_t) (UINT_MAX<=0xffffffff ? s1 * s2 : (unsigned)s1 * (unsigned)s2); 

无论如何,看起来像添加一些types定义到<stdint.h>保证不小于int将按顺序;-)。