C++ C+中两个整数的乘法+;

C++ C+中两个整数的乘法+;,c++,int,range,long-integer,C++,Int,Range,Long Integer,我有一个相当基本的问题,但我不确定我是否理解这个概念。假设我们有: int a = 1000000; int b = 1000000; long long c = a * b; 当我运行此命令时,c显示负值,因此我也将a和b更改为long,然后一切正常。那么,当它们的值在int范围内并且它们的乘积被分配给c(即long)时,为什么我必须更改a和b 我使用的是C/C++在乘法之前,ints没有提升到long,它们仍然是ints和乘积。然后产品被强制转换为long,但是太晚了,溢出已经发生 使用a

我有一个相当基本的问题,但我不确定我是否理解这个概念。假设我们有:

int a = 1000000;
int b = 1000000;
long long c = a * b;
当我运行此命令时,
c
显示负值,因此我也将
a
b
更改为
long
,然后一切正常。那么,当它们的值在
int
范围内并且它们的乘积被分配给
c
(即
long
)时,为什么我必须更改
a
b


我使用的是C/C++

在乘法之前,
int
s没有提升到
long
,它们仍然是
int
s和乘积。然后产品被强制转换为
long
,但是太晚了,溢出已经发生


使用
a
b
long-long
中的一个也应该起作用,因为另一个会被提升。

对于算术运算符,结果的类型不取决于将结果赋给什么,而是取决于操作数的类型。对于算术运算符,在操作数上执行。这用于将操作数转换为公共类型,这意味着对于小于无符号/有符号int的类型,如果值可以匹配,则将它们升级为无符号/有符号int,在这种情况下,它们已经都是int,因此不需要转换。有关原因的详细信息,请参阅

我们现在已经定义了未定义的行为,因为有符号整数溢出是未定义的行为,这在C++标准草案的章节中涵盖:代码> 5代码/代码> [EXPR],它表示:

如果在表达式求值期间,结果未在数学上定义或不在 对于其类型的可表示值,行为未定义。注意:大多数现有的C++实现 忽略整数溢出。处理被零除,使用零除数形成余数,以及所有 浮点异常因机器而异,通常可通过库函数进行调整。-结束说明]

现在,我们每天都有清理程序来捕获这些类型的未定义行为,并且在运行时使用带有clang和gcc的
-fsanitize=undefined
将捕获这些行为,并出现以下错误():

运行时错误:有符号整数溢出:1000000*1000000不能为空 以“int”类型表示

参考章节
5.6
[expr.mul]说:

[…]通常的算术转换是在操作数上执行的 并确定结果的类型

第5节说:

否则,应在两个操作数上执行积分提升(4.5)。61然后执行以下操作 规则应适用于提升的操作数

  • 如果两个操作数的类型相同,则无需进一步转换

这有点荒谬,因为汇编指令总是计算

int*int->64位长

因此,如果您查看机器代码,您会看到: imul 将64位存储到eax edx中 然后 干熄焦 将eax的位符号放入edx(从而丢失完整的64位结果) 然后eax edx存储到64位变量中

如果在乘法之前将32位的值转换为64位,则会无缘无故地调用64位乘法函数


(我检查过:代码优化时不是这样)

整数在乘法之前不会提升很长时间,它们仍然是整数和乘积。然后将产品浇铸到long long,但为时已晚,溢出已经发生。让a或b中的一个长也可以,另一个也可以提升。您应该标记正在使用的编程语言,因为不同的语言可能会引入不同的行为;)根据您使用的计算机和C的版本,“int”的大小也可能会改变;这是我先前的一个问题的翻版,我100%同意。1992年,我在第一份工作中学习了整数晋升规则,但C/C++的这种行为有时会让我感到困惑。FWIW,ARM汇编程序的行为与您描述的英特尔汇编程序的行为类似。当目标为64位时,将2个32位操作数相乘可以正常工作。正是语言规则破坏了数学。