问题标签 [integer-promotion]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票
4 回答
7274 浏览

c++ - 位运算符和整数提升是怎么回事?

我有一个简单的程序。请注意,我使用了一个大小为 1 字节的无符号固定宽度整数。

我的输出如下。

我测试了更大的数字,操作员<<总是给我正数,而操作员~总是给我负数。然后我用sizeof()了,发现...

当我使用左移位运算符(<<)时,我收到一个无符号的 4 字节整数。

当我使用按位非运算符(~)时,我收到一个有符号的 4 字节整数。

似乎按位非运算符(~)像算术运算符一样进行有符号整数提升。然而,左移运算符 ( <<) 似乎提升为无符号积分。

我觉得有义务知道编译器何时在我背后改变了一些东西。如果我的分析是正确的,所有按位运算符是否都提升为 4 字节整数?为什么有些签名有些未签名?我很混乱!

编辑:我总是得到积极或总是得到负值的假设是错误的。但是由于错误,我了解了真正发生的事情,这要归功于下面的精彩答案。

0 投票
0 回答
242 浏览

c++ - 使用十六进制常量的整数提升

我这里有一些代码:

打印“假”。我不太担心,因为我可以相信 foo 包含 0xE7,它现在​​被认为是有符号的 (-25) 并将 false 与 0xE7 进行比较——十进制为 231。


但是这个呢?

那打印“真实”。

根据 C++ 标准:

十六进制整数文字(以 16 为基数)以 0x 或 0X 开头,由一系列十六进制数字组成,其中包括十进制数字和字母 a 到 f 和 A​​ 到 F,十进制值到 15。

和:

整数文字的类型是表 6 中可以表示其值的相应列表中的第一个。

列表中的第一项是“int”。由于比较是正确的,因此看起来 0xFFFFFFE7 == -25。


但是,0xFFFFFFE7 是 4294967271 的另一种写法。所以我们试试:

打印“假”。因此,0xFFFFFFE7 与 4294967271 不同。

回到标准,“可以表示其价值”这句话的真正含义是什么?显然,您可以将 0xFFFFFFE7 填充到 4 字节有符号整数中,但这并不是真正“代表值”4294967271。

然而:

还:

所以看起来“它的值可以表示”意味着“在二进制级别”,而不是“将十六进制常量视为其等效的十进制数”。这听起来对吗?

在 gcc 4.8.2、Ubuntu 14.04、64 位处理器上测试。

0 投票
2 回答
473 浏览

c++ - 如何覆盖枚举的积分提升规则?

这与Are C++ enums signed or unsigned?. 根据 JavaMan 的回答, an enumis notsigned也不是unsigned。但它确实遵循整体提升规则。

我正在使用一个库,该库使用enums然后将它们传递给其他主要unsigned类型的类对象(如unsigned intsize_t)。由于语言规则,启用-Wsign-conversion警告以捕捉合法错误会导致许多误报。

这种规则创造了一种难以确保类型安全和捕捉常见错误的情况。这很困难,因为我想避免static_cast在整个代码中随意散布之类的东西。

有没有办法覆盖语言的默认行为以提升enums为具体signedunsigned类型?(类似于您可以指定 achar是有符号还是无符号的方式)。


相关的是,该库是在 1990 年代编写的,因此它支持许多较旧的编译器。如果解决方案甚至可以解决 C++03 甚至更早的问题,那就太好了。

如何保护 C++03 和 C++11 的移动构造函数?,我知道在实践中没有可靠的方法来检测其他 C++ 语言变体何时生效。在使用 Clang 3.5-std=c++03-std=c++11.

0 投票
3 回答
907 浏览

c - Promotion when evaluating constant integer expressions in preprocessor directives - GCC

NOTE: See my edits below.

ORIGINAL QUESTION:

Came across some curious behaviour which I cannot reconcile:

When compiled:

This suggests that the preprocessor follows different type promotion rules when evaluating constant integer expressions. Namely that, when an operator has operands of mixed sign, the signed operand is changed to an unsigned operand. The opposite is (generally) true in C.

I can find nothing in the literature to support this, but it's possible (likely?) that I haven't been thorough enough. Have I missed something? Is this behaviour correct?

As it stands, it seems at though any conditional expression in an #if or #elif directive which involves an explicitly unsigned integer constant may fail to behave as expected, i.e. as it would in C.


EDIT: As per my comments in Sourav Ghosh's answer, my confusion originally stemmed from expressions which included constants specified with L and LL suffixes. The example code I included in my original question was too simplified. Here is a better example:

Building:

This seems to violate the clause in 6.3.1.8 subsequent to the one posted by Sourav Ghosh (my emphasis):

Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.

It seems to violate this clause because -5LL has a rank which is higher than 0uL, and because the type of the first (signed long long) can indeed represent all of the values of the type of the second (unsigned long). The catch is, the preprocessor doesn't know this.

As mentioned in https://gcc.gnu.org/onlinedocs/gcc-3.0.2/cpp_4.html (my emphasis):

The preprocessor calculates the value of expression. It carries out all calculations in the widest integer type known to the compiler; on most machines supported by GCC this is 64 bits. This is not the same rule as the compiler uses to calculate the value of a constant expression, and may give different results in some cases. If the value comes out to be nonzero, the `#if' succeeds and the controlled text is included; otherwise it is skipped.

What seems to be implied by "carries out all calculations in the widest integer type known to the compiler" is that the operands themselves are treated as though they are specified as that same 'widest' type. In other words, -5 and -5L are treated as though they are -5LL, and 0u and 0uL are treated as though they are 0uLL. This activates the clause quoted by Sourav Ghosh, and leads to the observed behaviour.

In effect, there is only one rank as far as the preprocesser is concerned, so type promotion rules which depend upon operands with different rank are ignored. Is this not indeed different from how the compiler evaluates expressions?


EDIT #2: Here's a real-world example of how the same expression is evaluated differently by the preprocessor than it is by the compiler (taken from Optiboot).

Building for an AVR target:

Note how F_CPU was specified as a signed constant.

This works as expected. Examining the object file:

... shows that the expected values are assigned. Namely, baud_setting gets 8, baud_actual gets 111111, and baud_error gets -3.

Now we build with F_CPU defined as an unsigned constant (as is customary on this target):

The reported error is of the wrong magnitude, and the wrong sign.

Examination of the object file shows it to be identical to the one built with a signed value for F_CPU.

None of this is a surprise now, with the understanding that the preprocessor treats all constants as either the signed or unsigned variant of the widest integer type.

The surprise is that this isn't explicitly mentioned in either the standard, nor the GCC docs (that I can find).

Yes, the C rules for evaluating operands are followed exactly by the preprocessor, but only insofar as the case where both operands of a binary operator are of the same rank. I cannot find any text in the standard which states that the preprocessor treats all constants specified with or without L or LL as though they were all LL before the rules for integer promotions specified in 6.3.1.8 are enforced, nor can I find any mention of this behaviour in the GCC docs. The closest is the passage from the GCC docs quoted above stating that the preprocessor "carries out all calculations in the widest integer type known to the compiler".

This does not (should not) explicitly mean that the operands are treated as though they were specified with suffixes designating them as the widest integer type known to the compiler. Indeed, absent an explicit passage on the subject, my expectation would be that the operands would be subject to the same type conversion and integer promotion rules to which all operands are subject when evaluated by the compiler. This doesn't seem to be the case. The implication, based on the tests above, is that the application of the normal C integer promotion rules comes after the preprocessor promotes the operands to the widest (signed or unsigned) integer type known to the compiler.

If someone can show any explicit and relevant text on this subject, either from the standard or the GCC docs, I'm interested.


EDIT #3: note: I've copied the below paragraphs from the comments section into the post itself, since there were too many comments for it to be seen.

If someone can show any explicit and relevant text on this subject, either from the standard or the GCC docs, I'm interested.

Here's some text from 6.10.1:

  1. For the purposes of this token conversion and evaluation, all signed integer types and all unsigned integer types act as if they have the same representation as, respectively, the types intmax_t and uintmax_t defined in the header <stdint.h>.

That would seem to clinch it.

0 投票
3 回答
400 浏览

c - Char 和 int16 数组元素都显示为 32 位十六进制?

在下面的示例中:

输出:

为什么两者都打印 32 位值charint16_t为什么它们可以进行比较并被认为是相同的?

0 投票
1 回答
6148 浏览

c++ - uint8_t 操作,它们何时溢出?

我不确定在使用无符号字符时何时必须担心溢出。这个案例很清楚:

但是,这里会发生什么:

在做减法之前,a 和都被转换为浮点数吗?

或者在这种情况下:

所有三个变量都转换为浮点数吗?

是否有可能发生溢出的情况,即使分配给的变量是浮点数?如果e是浮点数、整数或其他任何值,规则是否相同?

此外,在这种情况下会发生什么:

0 投票
2 回答
178 浏览

c - uint16_t 减法 GCC 编译错误

我有以下程序

案例 A编译没有错误。
案例B导致以下错误
[error: conversion to ‘uint16_t’ from ‘int’ may alter its value [-Werror=conversion]]

我正在使用的警告选项是:
-Werror -Werror=strict-prototypes -pedantic-errors -Wconversion -pedantic -Wall -Wextra -Wno-unused-function

我在 Ubuntu 15.04 64 位上使用 GCC 4.9.2。

为什么我在案例 B中收到此错误,但在案例 A中没有?

PS:我用 clang 编译器运行了相同的示例,两种情况都编译得很好。

0 投票
4 回答
440 浏览

c++ - 字符是否在 C 表达式中自动提升?

我向我的一位同事发表了声明,内容是:

“在 C 表达式中,字符会自动提升为整数,这对性能来说很好,因为 CPU 以其自然字长运行得最快。

我相信由于 char 的等级,在标准中的某处说明了 char 提升行为。

这是我得到的回复:

“字符不会默认提升为整数。寄存器大小为 32 位,但作为编译器实现,可以将一行中的多个字节值打包到单个寄存器中。这并不总是可以预测的。唯一一次可以验证自动提升是在没有包裹结构的情况下将类型传递到调用堆栈,因为 C 标准正式需要调用堆栈内存中的 32 位值。大量 CPU 架构已经针对非 32 位值优化了汇编调用,所以没有在这种情况下,可以对 CPU 或编译器做出假设。”

我不确定谁是对的,以及该相信什么。事实是什么?

0 投票
1 回答
444 浏览

c++ - 大小小于 int 的位域应该是整体提升的主题吗?

假设我有以下内容struct

我感兴趣的是表达的类型a + b。虽然从技术上讲,位域的“类型”的大小小于int可能应该发生整体提升,然后结果int就像它恰好在 gcc 和 clang 中一样。

但是由于不可能提取位域本身的确切类型并且它总是被推断为它的“大”类型(即unsigned int在这种情况下),所以应该进行整体提升是否正确?因为我们实际上不能谈论位域的确切类型和它们的大小,除非它们被推断为unsigned int在这种情况下不应该发生积分提升。

(我的问题再次源于 MSVC 碰巧认为这unsigned int是这种表达的类型)

0 投票
2 回答
721 浏览

c - C sprintf 使用字节参数中断(Keil 编译器)

我有在 2 个项目/平台中运行的代码。它适用于一个,而不是另一个。代码是这样的:

预期输出为“1”(gcc),但在一个编译器(Keil)上,它返回“511”,十六进制为 0x1FF。看起来它没有使用此编译器将字节提升为 int。这已得到确认,因为如果我这样做,它可以正常工作:

我的问题是:为什么一个编译器会做我认为“正确的事情”,而另一个却没有?是我不正确的期望/假设、编译器设置还是其他原因?