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 */
在所有的下面,我假设编译器不能对s1
或s2
的范围有任何的先入之见,初始值设定项仅用于上面的一个例子。
如果我在一个整数大小为32位的编译器(例如编译x86时)上编译这个,没问题。 编译器会简单地使用s1
和s2
作为uint32_t
types的值(不能进一步提升它们),乘法只会给出结果,如注释所示(模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 int
types的expression式。
v = 1U * s1 * s2;
这要么将1U
转换为uint32_t
,要么将s1
和s2
为unsigned int
,具体取决于适合您特定平台的内容。
@Deduplicator评论说,一些编译器,其中uint32_t
比unsigned int
窄,可能会警告赋值中的隐式转换,并指出这样的警告可能通过明确地进行转换而被抑制:
v = (uint32_t) (1U * s1 * S2);
在我看来,它看起来不那么优雅。
恭喜find摩擦点。
一个可能的方法:
v = (uint32_t) (UINT_MAX<=0xffffffff ? s1 * s2 : (unsigned)s1 * (unsigned)s2);
无论如何,看起来像添加一些types定义到<stdint.h>
保证不小于int
将按顺序;-)。