英文:
Braced Initialization Error with 32bit GCC for Unsigned Long Long
问题
When I compile the code on Godbolt with ARM32-GCC, I get the errors because the integer constant is so large that it is unsigned.
I do not get this error when using ARM64-GCC (or 32bit/64bit Clang or 32bit/64bit MSVC). But I get the error when using other 32bit GCC variants on Godbolt.
Why is this an error with these 32bit GCC compilers only?
You can fix it by using 18446744073709551615u
. There is no other way to fix this error without changing the code and without disabling this warning/error in general (-Wno-narrowing
).
英文:
When I compile the code
typedef long long unsigned myuint64;
myuint64 a[2] = {18446744073709551615, 18446744073709551615}; // error on 32bit gcc
myuint64 b = 18446744073709551615; // no error
on Godbolt with ARM32-GCC I get the errors
(3 warnings for "integer constant is so large that it is unsigned")
error: narrowing conversion of '-1' from 'long long int' to 'myuint64' {aka 'long long unsigned int'} [-Wnarrowing]
6 | myuint64 a[2] = {18446744073709551615, 18446744073709551615};
| ^~~~~~~~~~~~~~~~~~~~
error: narrowing conversion of '-1' from 'long long int' to 'myuint64' {aka 'long long unsigned int'} [-Wnarrowing]
6 | myuint64 a[2] = {18446744073709551615, 18446744073709551615};
(The number is the maximum value for uint64)
I do not get this error when using ARM64-GCC (or 32bit/64bit Clang or 32bit/64bit MSVC).
But I get the error when using other 32bit GCC variants on Godbolt.
Why is this an error with these 32bit GCC compilers only?
I can fix it by using 18446744073709551615u
. Is there any other way to fix this error without changing the code and without disabling this warning/error in general (-Wno-narrowing
)?
答案1
得分: 3
你的代码能否编译取决于是否存在__int128
,它仅适用于ARM64 GCC。原因是,当你写一个没有后缀的整数字面值,比如u
,编译器会从以下类型列表中选择,直到值适应其中一个类型:
int
long int
long long int
- 一些实现定义的扩展有符号整数类型,如
__int128
你的值18446744073709551615
等于264-1,因此64位有符号整数无法容纳它,但__int128
可以。
让我们看看ARM64 gcc为你的整数字面值提供了什么类型:
// 警告:整数常量太大,以致于它是无符号的
void foo(decltype(18446744073709551615)) {}
foo(__int128):
ret
警告消息与实际发生的情况不符:你可以看到编译器输出了一个接受__int128
的函数,这是你的字面值的类型。
同样的代码使用ARM gcc(32位)编译会产生:
foo(long long):
bx lr
在这种情况下,字面值太大,无法适应任何类型,__int128
不存在(尝试使用它会产生编译器错误),GCC选择将其视为警告而不是错误。
为什么你的代码只在64位编译器上工作
myuint64 a[2] = {18446744073709551615, 18446744073709551615};
GCC错误地为你的字面值选择了long long
,尽管这段代码是不合法的,并将它们转换为-1
。myuint64
是无符号的,所以它无法表示-1
,而且列表初始化不允许缩小转换。结果,我们得到了编译器错误。
在64位编译器上使用__int128
就没有问题,因为我们可以很好地将__int128(18446744073709551615)
转换为myuint64(18446744073709551615)
。
结论
你发现了一种异常情况,其中编译器诊断实际上是反直觉的。忽略这些诊断并理解__int128
的参与会揭示问题。
我不知道有任何编译器标志可以用来修复这个问题,所以我建议对字面值使用u
后缀,这样编译器会从以下类型中选择:
unsigned int
unsigned long int
unsigned long long int
- 一些实现定义的扩展无符号整数类型,例如
__uint128
。
英文:
Whether your code compiles or not depends on whether __int128
exists, and it only exists for ARM64 GCC. The reason is that when you write an integer-literal with no suffix such as u
, the compiler chooses a type from this list until the value fits in one of these types:
int
long int
long long int
- some implementation-defined extended signed integer type, e.g.
__int128
See [lex.icon] §3.
Your value 18446744073709551615
equals 2<sup>64</sup>-1, so a 64-bit signed integer cannot fit it, but __int128
can.
Let's reveal what type ARM64 gcc gives to your integer literal:
// warning: integer constant is so large that it is unsigned
void foo(decltype(18446744073709551615)) {}
foo(__int128):
ret
The warning message doesn't match what really happens: You can see that the compiler outputs a function accepting __int128
, which is the type of your literal.
The same code compiled with ARM gcc (32-bit) produces:
foo(long long):
bx lr
In this case, the literal is too large to fit any of the types, __int128
doesn't exist (and attempting to use it would produce a compiler error), and GCC chooses to treat this as a warning, not an error.
Why your code only works with 64-bit compilers
myuint64 a[2] = {18446744073709551615, 18446744073709551615};
GCC falsely chooses long long
for your literals even though this code is ill-formed, and converts them to -1
. myuint64
is unsigned, so it cannot represent -1
, and list initialization does not allow narrowing conversion. As a result, we get a compiler error.
With __int128
on a 64-bit compiler, this is not a problem, because we can convert __int128(18446744073709551615)
to myuint64(18446744073709551615)
just fine.
Conclusion
You have discovered an unusual situation where compiler diagnostics are really counter-intuitive. Ignoring these diagnostics and understanding that __int128
is involved reveals the problem.
I am not aware of any compiler flags that you can use to fix this, so I recommend using the u
suffix for literals, in which case the compiler chooses from:
unsigned int
unsigned long int
unsigned long long int
- some implementation-defined extended unsigned integer type, e.g.
__uint128
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论