假设我有以下变量和以下等式:
int16_t a,b,c,d;
int32_t result;
result = a*b - c*d;
b和c d的中间结果将以16位还是32位存储?
PS:我可以比我写问题更快地测试这个,我想知道C规范说的是什么。
答案 0 :(得分:4)
中间结果的类型为int
。
首先会提升比int
更窄的任何类型。这些整数促销适用于int
或unsigned
**类型。因此,数学必须出现在int
,unsigned
或原始类型。
int16_t
肯定比int
更窄或相同。
result
的类型与中间结果的类型无关
int16_t a,b,c,d;
int32_t result = a*b - c*d;
为所有平台提供便携,包括int
更窄int32_t
的平台,确保使用至少32位数学计算产品。
#include <stdint.h>
int32_t result = INT32_C(1)*a*b - INT32_C(1)*c*d;
当然结果是存储为32位,可能的符号扩展int
中间结果。
对于具有32位或64位int
的计算机,中间结果将适合int32_t
,而值没有任何变化,没有异常。结果为-2147450880 to 2147450880
(80008000至7FFF8000)。
**从不long
,甚至不在unicorn平台上。
答案 1 :(得分:3)
我很快就会更新此答案。我不再相信该标准允许将int16_t
提升为long
。但在一些极其模糊的案例中,它可以推广到unsigned int
。对于奇异系统,整数转换排名规则有一些奇怪的结果。
chux's answer 几乎正确无误。有一些模糊且不太可能的情况,其中间结果是long int
类型。
int16_t
必须是16位二进制补码整数类型,没有填充位。类型为int16_t
的操作数将被提升为一种类型,该类型可以表示int16_t
类型的所有可能值,并且至少与int
一样宽。
标准要求int
的范围至少为-32767到+32767。
假设int
使用1的补码或符号和幅度或表示,它使用2的补码表示,但通常为-32768
的表示形式将被处理作为陷阱表示。然后int
无法保存int16_t
类型的所有值,并且必须将操作数提升为long
(保证范围足够宽)。< / p>
要实现这一点,实现必须支持两个 int
类型,具有受限制的16位范围(很可能不是2的补码)和一种适合int16_t
的类型,意味着它具有2的补码表示,没有填充位或陷阱表示。例如,1的补码系统更可能没有这样的类型,因此它根本不会定义int16_t
(尽管它会定义int_fast16_t
和int_least16_t
)
对于几乎所有实际实现,int
都可以包含int16_t
类型的所有值,因此中间结果的类型为int
。对于几乎所有剩余的现实世界或假设系统,将不提供int16_t
。对于假设的一小部分不存在但符合C的实现,中间结果的类型为long int
。
更新: chux指出我的论点中可能存在的弱点。在评论中,他认为N1570 6.2.6.2第2段说明整数类型可以使用二进制补码表示,一个补码,或符号和大小,旨在要求所有整数类型使用相同的表示(当然,位数不同,但都使用相同的那些选择)。
J.3.5中非规范性文本的措辞,说:
是否使用符号和幅度表示有符号整数类型, 两个补码,或者说是“补码”,是否非同寻常 value是陷阱表示或普通值
是实现定义的,倾向于支持解释。如果不同的整数类型在这方面可能不同,则应该为每个整数类型说。
然而:
6.2.6.2p2 显式表示所有整数类型必须使用相同的表示形式,并且我不知道其他任何意味着它们必须这样做。< / p>
支持具有不同表示形式的整数类型可能很有用。例如,硬件可能支持补码,但为了依赖于int16_t
等的代码,实现可能在软件中支持两个补码整数。或者硬件可能直接支持两种表示。 (我的猜测是作者没有考虑这种可能性,但我们只能遵循标准实际所说的内容。)
在任何情况下,实际上都不需要调用非二进制补码表示来构建int16_t
提升为long int
的情况。
假设如下:
INT_MAX == +32767
INT_MIN == -32767
int
,所有值位0都是陷阱表示。 int
没有填充位,但通常表示-32768
的位模式是陷阱表示。 N1570 6.2.6.2第2段明确允许这样做。
但int16_t
的范围必须为-32768
至+32767
。 7.20.2.1表示INTN_MIN
必须完全 - (2 N-1 )(在这种情况下为N==16
)。
因此,在此几乎完全难以置信的但符合规定的实施中,int16_t
已定义,但int
无法代表int16_t
的所有值,因此{{1} }值被提升为int16_t
(必须至少为32位)。