Fab*_*llo 149 c++ math types integer-overflow literals
long long int n = 2000*2000*2000*2000; // overflow
long long int n = pow(2000,4); // works
long long int n = 16000000000000; // works
Run Code Online (Sandbox Code Playgroud)
为什么第一个溢出(乘以整数文字常量以分配给 long long)?
它与第二个或第三个有什么不同?
Qui*_*mby 140
因为2000
是一种int
通常是32位的。只需使用2000LL
.
@AdrianMole 在评论中建议使用LL
后缀代替ll
,现在已删除。请检查他的回答。
默认情况下,整数文字是可以保存其值但不小于 的最小类型int
。2000
可以很容易地存储在 int 中,因为标准保证它至少是一个有效的 16 位类型。
算术运算符总是使用存在的较大类型但不小于 来调用int
:
char*char
将被提升为 operator*(int,int)->int
char*int
电话 operator*(int,int)->int
long*int
电话 operator*(long,long)->long
int*int
仍然打电话operator*(int,int)->int
。类型不依赖于结果是否可以存储在推断类型中。这正是您的情况发生的问题 - 乘法是用int
s完成的,但结果溢出,因为它仍然存储为int
.
C++ 不支持像 Haskell 那样基于目的地推断类型,因此赋值无关紧要。
Adr*_*ica 93
第一行代码的 RHS 上的常量(文字)是int
值(不是 long long int
)。因此,乘法是使用int
算术进行的,这会溢出。
要解决此问题,请long long
使用LL
后缀创建常量:
long long int n = 2000LL * 2000LL * 2000LL * 2000LL;
Run Code Online (Sandbox Code Playgroud)
事实上,如在由注释指出彼得科尔德,该LL
后缀是实际上只需要在所述第一(最左边)或第二恒定。这是因为,当两个不同等级的类型相乘时,较低等级的操作数会提升为较高等级的类型,如下所述:C++ 运算符中的隐式类型转换规则。此外,由于*
(乘法)运算符具有从左到右的关联性,第一次乘法的“提升”结果将该提升传播到第二次和第三次。
因此,以下任一行也不会溢出:
long long int n1 = 2000LL * 2000 * 2000 * 2000;
long long int n2 = 2000 * 2000LL * 2000 * 2000;
Run Code Online (Sandbox Code Playgroud)
注意:虽然小写后缀(如2000ll
)是有效的C ++和完全明确的编译器,有一个一般的共识是,小写字母“ELL”,应在避免long
和long long
整数常量,因为它可以很容易被误认为,由人类读者,对于数字,1
。因此,您会注意到2000LL
(大写后缀)已在此处提供的所有答案中使用。
Wer*_*nze 49
2000*2000*2000*2000
是 4 个int
值的乘积,它返回一个int
值。当您将此int
值分配给long long int n
已经发生的溢出时(如果int
是 32 位,则结果值将不适合)。
你需要确保不会发生溢出,所以当你写
long long int n = static_cast<long long int>(2000)*2000*2000*2000;
Run Code Online (Sandbox Code Playgroud)
你确保你正在做long long int
乘法(long long int
乘以int
返回 a long long int
,所以在你的情况下没有溢出)。
更短(更好)的方法是编写2000LL
或2000ll
代替static_cast
. 这为整数文字提供了正确的类型。这对于适合 2000int
的int
.
long long int n = 2000LL*2000*2000*2000;
long long int n = 2000LL*2000LL*2000LL*2000LL;
Run Code Online (Sandbox Code Playgroud)
Rus*_*lan 29
其他答案(截至撰写本文时)似乎不够明确,无法回答所述问题。我会努力填补这个空白。
为什么第一个溢出(乘以整数文字常量以分配给 long long)?
表达方式
long long int n = 2000*2000*2000*2000;
Run Code Online (Sandbox Code Playgroud)
评估如下:
long long int n = ((2000*2000)*2000)*2000;
Run Code Online (Sandbox Code Playgroud)
步骤在哪里(假设 32 位int
):
(2000*2000)
是两个int
值的乘积,产生 4000000,另一个int
值。((2000*2000)*2000)
是上面产生的int
值 4000000 与int
值 2000的乘积。如果该值可以放入int
. 但是我们假设的 32 位 int 可以存储最大值 2 31 -1=2147483647。所以我们在这一点上就溢出了。int
乘积的分配将发生(如果不是溢出)到long long
变量,这将保留该值。由于我们确实有溢出,该语句具有未定义的行为,因此不能保证第 3 步和第 4 步。
它与第二个或第三个有什么不同?
long long int n = pow(2000,4);
该pow(2000,4)
转换2000
和4
成double
(见一些文档pow
),然后函数实现会尽全力产生结果的一个很好的近似,作为一个double
。然后赋值将此double
值转换为long long
.
long long int n = 16000000000000;
文字16000000000000
太大而无法放入int
,因此它的类型是下一个可以适合该值的有符号类型。它可能是long
或long long
,具体取决于平台。有关详细信息,请参阅整数文字#文字的类型。然后赋值将此值转换为long long
(或仅写入它,如果文字的类型long long
已经存在)。
Dea*_*son 19
第一个是使用整数(通常为 32 位)的乘法。它溢出是因为这些整数不能存储2000^4
。然后将结果强制转换为long long int
。
第二个调用 pow 函数,该函数将第一个参数强制转换为double
并返回 a double
。然后将结果强制转换为long long int
。在这种情况下没有溢出,因为数学是在 double 值上完成的。
您可能希望在 C++ 中使用以下内容来理解这一点:
#include<iostream>
#include<cxxabi.h>
using namespace std;
using namespace abi;
int main () {
int status;
cout << __cxa_demangle(typeid(2000*2000*2000*2000).name(),0,0,&status);
}
Run Code Online (Sandbox Code Playgroud)
如您所见,类型为int
.
在 C 中,您可以使用(由):
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#define typename(x) _Generic((x), /* Get the name of a type */ \
\
_Bool: "_Bool", unsigned char: "unsigned char", \
char: "char", signed char: "signed char", \
short int: "short int", unsigned short int: "unsigned short int", \
int: "int", unsigned int: "unsigned int", \
long int: "long int", unsigned long int: "unsigned long int", \
long long int: "long long int", unsigned long long int: "unsigned long long int", \
float: "float", double: "double", \
long double: "long double", char *: "pointer to char", \
void *: "pointer to void", int *: "pointer to int", \
char(*)[]: "pointer to char array", default: "other")
unsigned int a = 3;
int main() {
printf("%s", typename(a-10));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这里表达式unsigned int
的类型是因为类型不匹配隐式将类型升级为unsigned int
和之间的最大类型int
,即unsigned int
。的unsigned int
下溢到一个大的正,当分配给或解释为这将是预期的负int
。计算结果将始终unsigned int
与所涉及的值无关。
C
没有后缀的整数文字的最小默认类型是int
,但只有当文字超过此值时,其类型才会变为unsigned int
; 如果大于它,则给出 a 的类型long int
,因此 2000s 都是int
s。然而,在文字上执行的表达式的类型,使用一元或二元运算符,使用隐式类型层次结构来决定类型,而不是结果的值(不像文字本身,它使用文字的长度来决定类型) . 为了解决这个问题,您必须ul
在 2000 年代使用长后缀来明确指定文字的类型。
同样,十进制文字的默认类型是double
,但这可以通过f
后缀更改。前缀不会改变十进制或整数文字的类型。
字符串文字的类型是char []
,尽管它实际上是const char []
, 并且只是该字符串文字 in 的实际表示中第一个字符.rodata
的地址,并且可以像使用一元与号的任何数组一样获取该地址&"string"
,这是与 相同的值(地址)"string"
,只是不同的类型(char (*)[7]
vs. char[7]
;"string"
即char[]
不仅仅是(在编译器级别)指向数组的指针,它是数组,而一元&符号仅提取指向数组的指针)。的u
前缀改变这对的阵列char16_t
,这是一种unsigned short int
; 的U
前缀它改变到的阵列char32_t
,这是一种unsigned int
; 并且L
前缀将其更改为一个数组wchar_t
这是一个int
. u8
is achar
并且一个不带前缀的字符串使用实现特定的编码,它通常与u8
即 UTF-8相同,其中 ASCII 是一个子集。仅可用于字符串文字的原始 ( R
) 前缀(并且仅在 GNU C(std=gnu99
以后)上可用)可以作为前缀 ieuR
或u8R
,但这不会影响类型。
字符文字的类型是int
除非以u
( u'a'
is unsigned short int
) 或U
( U'a'
is unsigned int
)为前缀。u8
和 和L
都int
用于字符文字。字符串或字符文字中的转义序列不会影响编码,因此不会影响类型,它只是将要编码的字符实际呈现给编译器的一种方式。
复杂文字10i+1
or 10j+1
is的类型complex int
,其中实部和虚部都可以有一个后缀,例如10Li+1
,在这种情况下,这使虚部变长,整体类型为complex long int
,并升级了实部和虚部的类型部分,所以你把后缀放在哪里或者你是否把它放在两者上都没有关系。不匹配将始终使用两个后缀中最大的作为整体类型。
使用显式转换而不是文字后缀总是会导致正确的行为,如果您正确使用它并且知道它截断/扩展的语义差异(符号扩展为signed
;零扩展为unsigned
– 这基于文字的类型或表达式被强制转换而不是被强制转换的类型,因此 asigned int
被符号扩展为unsigned long int
) 一个文字到该类型的表达式,而不是文字本身具有该类型。
C++
同样,最小默认类型是int
最小文字基数的an 。文字基础即文字的实际值,后缀根据下表影响最终文字类型,其中在每个后缀的每个框中,最终类型的顺序根据实际的大小从最小到最大列出字面基础。对于每个后缀,文字的最终类型只能等于或大于后缀类型,并基于文字库的大小。C 表现出相同的行为。当大于 a 时long long int
,取决于编译器,__int128
使用。我认为您还可以创建自己的文字后缀运算符i128
并返回该类型的值。
十进制文字的默认类型与 C 相同。
字符串文字的类型是char []
. 的类型的&"string"
是const char (*) [7]
和类型+"string"
IS const char *
(在C只能衰减使用"string"+0
)。C++ 的不同之处在于后两种形式获取 aconst
但在 C 中它们没有。字符串前缀的行为与 C 中相同
字符和复杂文字的行为与 C 相同。
归档时间: |
|
查看次数: |
10915 次 |
最近记录: |