优化和严格的混叠

发布于 2025-01-28 22:15:32 字数 1354 浏览 1 评论 0原文

我的问题是关于代码片段的问题,例如:

#include <iostream>

int main() {
    double a = -50;
    std::cout << a << "\n";
    uint8_t* b = reinterpret_cast<uint8_t*>(&a);
    b[7] &= 0x7F;
    std::cout << a << "\n";
    return 0;
}

据我所知,我没有违反任何规则,并且一切都很好地定义(如下所述,我忘记了uint8_t不允许别名其他类型)。有一些实施定义的行为正在进行,但是出于这个问题的目的,我认为这是不相关的。

我希望此代码在double遵循IEEE标准的系统上打印-50,然后50,是8 字节长,以小末日格式存储。现在的问题是。编译器是否可以保证这种情况发生。更具体地说,通过整个函数将a通过整个功能中的a,可以显式或隐式地打开优化的编译器,以明确或隐式优化中间b [7]。显然,可以通过指定挥发性双重A来解决第二个问题,但这是需要的吗?

编辑:作为注释I(错误地)记住,uint8_t被要求是无符号char的别名,但实际上该标准并未指定此类。我还以一个方式写了一个问题,是的,是的,编译器可以提前知道这里的所有内容,但是修改为

#include <iostream>

int main() {
    double a;
    std::cin >> a;
    std::cout << a << "\n";
    unsigned char* b = reinterpret_cast<unsigned char*>(&a);
    b[7] &= 0x7F;
    std::cout << a << "\n";
    return 0;
}

一个人可以看到问题可能出现的位置。这里不再违反严格的别名规则,a不是编译时间常数。但是,理查德·克里特(Richard Critten)的评论很好奇是否可以检查并没有书写的别名数据,在仍然遵循标准的同时,是否有一种方法可以设置单个字节?

My question is regarding a code fragment, such as below:

#include <iostream>

int main() {
    double a = -50;
    std::cout << a << "\n";
    uint8_t* b = reinterpret_cast<uint8_t*>(&a);
    b[7] &= 0x7F;
    std::cout << a << "\n";
    return 0;
}

As far as I can tell I am not breaking any rules and everything is well defined (as noted below I forgot that uint8_t is not allowed to alias other types). There is some implementation defined behavior going on, but for the purpose of this question I don't think that is relevant.

I would expect this code to print -50, then 50 on systems where the double follows the IEEE standard, is 8 bytes long and is stored in little endian format. Now the question is. Does the compiler guarantee that this happens. More specifically, turning on optimisations can the compiler optimise away the middle b[7], either explicitly or implicitly, by simply keeping a in a register through the whole function. The second one obviously could be solved by specifying volatile double a, but is that needed?

Edit: As an a note I (mistakenly) remembered that uint8_t was required to be an alias for unsigned char, but indeed the standard does not specify such. I have also written the question in a way that, yes the compiler can ahead of time know everything here, but modified to

#include <iostream>

int main() {
    double a;
    std::cin >> a;
    std::cout << a << "\n";
    unsigned char* b = reinterpret_cast<unsigned char*>(&a);
    b[7] &= 0x7F;
    std::cout << a << "\n";
    return 0;
}

one can see where the problem might arise. Here the strict aliasing rule is no longer violated, and a is not a compile time constant. Richard Critten's comment however is curious if the aliased data can be examined, but not written, is there a way one can set individual bytes, while still following the standard?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

幻梦 2025-02-04 22:15:32

更具体地说,打开优化的编译器可以通过整个功能将A放在寄存器中,可以明确或隐式地优化中间B [7]。

编译器可以将双值50作为常数生成,并将其直接传递到输出函数。 B可以完全优化。像大多数优化一样,这是由于AS-IF规则所致:

[Into.Abstract]

本文档中的语义描述定义了一个参数化的非确定抽象机。
本文档对符合实现的结构没有任何要求。
特别是,它们不需要复制或模仿抽象机的结构。
相反,需要符合实现来模仿(仅)抽象机的可观察行为,如下所述。


显然可以通过指定挥发性双A

来解决第二个。

从而阻止优化,通常将其视为与解决方案相反。


编译器保证[50被打印]。

您没有提及您要问的编译器。我假设您的意思是标准是否保证了这一点。它不能保证这一点。您依靠有关实现的几个假设:

  • 如果sizeof(double)&lt; 8,然后您访问对象的范围,并且程序的行为不确定。
  • 如果std :: uint8_t不是无符号char的类型别名,则不允许Alias double和该程序不确定。

鉴于假设的存在,因此behviour定义明确,因此第二个输出将为double值,就像-50,但其最重要的位(从8个向前)字节的位置(s)位置7将设置为0。如果Little Endian IEEE-754表示,则该值将为50。volatile不需要保证这一点,并且在以防万一的情况下不会添加保证该程序的行为不确定。

More specifically, turning on optimisations can the compiler optimise away the middle b[7], either explicitly or implicitly, by simply keeping a in a register through the whole function.

The compiler can generate the double value 50 as a constant, and pass that directly to the output function. b can be optimised away completely. Like most optimisation, this is due to the as-if rule:

[intro.abstract]

The semantic descriptions in this document define a parameterized nondeterministic abstract machine.
This document places no requirement on the structure of conforming implementations.
In particular, they need not copy or emulate the structure of the abstract machine.
Rather, conforming implementations are required to emulate (only) the observable behavior of the abstract machine as explained below.


The second one obviously could be solved by specifying volatile double a

That would prevent the optimisation, which would generally be considered to be the opposite of a solution.


Does the compiler guarantee that [50 is printed].

You didn't mention what compiler you are asking about. I'm going to assume that you mean whether the standard guarantees this. It doesn't guarantee that universally. You are relying on several assumptions about the implementation:

  • If sizeof(double) < 8, then you access the object outside of its bounds, and behaviour of the program is undefined.
  • If std::uint8_t is not an a type alias of unsigned char, then it isn't allowed to alias double, and the behaviour of the program is undefined.

Given the assumptions hold and thus behviour is well-defined, then the second output will be of a double value that is like -50, but whose most significant bit(s from 8th forward) of the byte at position 7 will have been set to 0. In case of little endian IEEE-754 representation, that value would be 50. volatile is not needed to guarantee this, and it won't add a guarantee in case the behaviour of the program is undefined.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文