简体   繁体   English

Float64 位问题冷杉取消引用类型双关指针将破坏严格别名规则 [-Wstrict-aliasing]

[英]Float64 bit issue fir dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

I'm trying the below code to get lower and higher 32-bit parts of Float64 bit value.我正在尝试使用下面的代码来获得Float64位值的越来越低的 32 位部分。

#define LSL_HI(x) *(1+(sInt32*)&x)
#define LSL_LO(x) *(sInt32*)&x

//warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing] 
sInt32 lx = LSL_LO(1111.2222); 

The [-Wstrict-aliasing] option is part of -O2 optimization and I don't have to disable this. [-Wstrict-aliasing]选项是-O2优化的一部分,我不必禁用它。

What is the solution to fix this issue?解决此问题的解决方案是什么?

This macro I have taken from GCC math lib itself.这个宏是我从 GCC 数学库本身中获取的。

If you want the literal bytes of a float 64 you can do this:如果您想要浮点数 64 的文字字节,您可以这样做:

#include <cstdint>
static_assert(double == 8, "non 64-bit double!");

int 
main()
{
    double x = 1111.2222;
    uint32_t lo = static_cast<uint32_t>(reinterpret_cast<uint64_t>(x) & 0x00000000FFFFFFFF);
    uint32_t hi = static_cast<uint32_t>((reinterpret_cast<uint64_t>(x) & 0xFFFFFFFF00000000) >> (8*4));
    return 0;
}

I don't think this is what you are trying to do.我不认为这就是你想要做的。 If you instead want the fractional part of the double and the whole part you will need to try something else.如果您想要 double 的小数部分和整个部分,您将需要尝试其他方法。

I'm not sure what you are trying to accomplish, same as what Dylan mentioned.我不确定你想要完成什么,就像迪伦提到的那样。

If you want to rip apart a double (assuming an IEEE 754 double), you could do this (I'm using ANSI escape sequences, which may be unsuitable for your environment).如果您想拆分双精度数(假设是 IEEE 754 双精度数),您可以这样做(我使用的是 ANSI 转义序列,这可能不适合您的环境)。 I've got both "ripping apart into hex bytes" and "ripping apart into sign / exponent / fraction":我已经“撕成十六进制字节”和“撕成符号/指数/分数”:

#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>

using std::cout;
using std::fpclassify;
using std::memcpy;
using std::nan;
using std::numeric_limits;
using std::reverse;
using std::setw;
using std::size_t;
using std::string;
using std::stringstream;
using std::uint32_t;
using std::uint64_t;

namespace {

uint32_t low32_from(double d) {
    char const* p = reinterpret_cast<char const*>(&d);
    uint32_t result;
    memcpy(&result, p, sizeof result);
    return result;
}

uint32_t high32_from(double d) {
    char const* p = reinterpret_cast<char const*>(&d);
    p += 4;
    uint32_t result;
    memcpy(&result, p, sizeof result);
    return result;
}

string hexstr(uint32_t value) {
    char hex[] = "0123456789ABCDEF";
    unsigned char buffer[4];
    memcpy(buffer, &value, sizeof buffer);
    auto p = &buffer[0];
    stringstream ss;
    char const* sep = "";
    for (size_t i = 0; i < sizeof buffer; ++i) {
        ss << sep << hex[(*p >> 4) & 0xF] << hex[*p & 0xF];
        sep = " ";
        ++p;
    }

    return ss.str();
}

string bits(uint64_t v, size_t len) {
    string s;
    int group = 0;
    while (len--) {
        if (group == 4) { s.push_back('\''); group = 0; }
        s.push_back(v & 1 ? '1' : '0');
        v >>= 1;
        ++group;
    }
    reverse(s.begin(), s.end());
    return s;
}

string doublebits(double d) {
    auto dx = fpclassify(d);
    unsigned char buffer[8];
    memcpy(buffer, &d, sizeof buffer);
    stringstream ss;
    uint64_t s = (buffer[7] >> 7) & 0x1;
    uint64_t e = ((buffer[7] & 0x7FU) << 4) | ((buffer[6] >> 4) & 0xFU);
    uint64_t f = buffer[6] & 0xFU;
    f = (f << 8) + (buffer[5] & 0xFFU);
    f = (f << 8) + (buffer[4] & 0xFFU);
    f = (f << 8) + (buffer[3] & 0xFFU);
    f = (f << 8) + (buffer[2] & 0xFFU);
    f = (f << 8) + (buffer[1] & 0xFFU);
    f = (f << 8) + (buffer[0] & 0xFFU);

    ss << "sign:\033[0;32m" << bits(s, 1) << "\033[0m ";
    if (s) ss << "(-) ";
    else ss << "(+) ";

    ss << "exp:\033[0;33m" << bits(e, 11) << "\033[0m ";
    ss << "(" << setw(5) << (static_cast<int>(e) - 1023) << ") ";


    ss << "frac:";

    // 'i' for implied 1 bit, '.' for not applicable (so things align correctly).
    if (dx == FP_NORMAL) ss << "\033[0;34mi";
    else ss << "\033[0;37m.\033[34m";

    ss << bits(f, 52) << "\033[0m";

    if (dx == FP_INFINITE) ss << " \033[35mInfinite\033[0m";
    else if (dx == FP_NAN) ss << " \033[35mNot-A-Number\033[0m";
    else if (dx == FP_NORMAL) ss << " \033[35mNormal\033[0m";
    else if (dx == FP_SUBNORMAL) ss << " \033[35mDenormalized\033[0m";
    else if (dx == FP_ZERO) ss << " \033[35mZero\033[0m";

    ss << " " << d;

    return ss.str();
}

} // anon

int main() {
    auto lo = low32_from(1111.2222);
    auto hi = high32_from(1111.2222);
    cout << hexstr(lo) << "\n";
    cout << hexstr(hi) << "\n";
    cout << doublebits(1111.2222) << "\n";
    cout << doublebits(1.0) << "\n";
    cout << doublebits(-1.0) << "\n";
    cout << doublebits(+0.0) << "\n";
    cout << doublebits(-0.0) << "\n";
    cout << doublebits(numeric_limits<double>::infinity()) << "\n";
    cout << doublebits(-numeric_limits<double>::infinity()) << "\n";
    cout << doublebits(nan("")) << "\n";

    double x = 1.0;
    while (x > 0.0) {
        cout << doublebits(x) << "\n";
        x = x / 2.0;
    }
}

The ability to cast a pointer and use the freshly-cast pointer to perform type punning "in a documented fashion characteristic of the environment" is one the Standards Committee referred to as a "popular extension" [see page 11 of the Rationale].转换指针并使用新转换的指针“以环境特征的文档化方式”执行类型双关的能力是标准委员会称为“流行扩展”的能力之一[参见基本原理的第 11 页]。 The Standard deliberately refrains from requiring that even compilers that are not intended to be suitable for low-level programming tasks (such as decomposing 64-bit floating-point values directly into 32-bit chunks) must support it, but instead it allows compilers to support such constructs or not, based upon customers' needs or any other criteria the compiler writers see fit, and it allows such constructs to be used in programs in programs that seek merely to be "conforming" rather than "strictly conforming".该标准特意避免要求即使是不适合低级编程任务(例如将 64 位浮点值直接分解为 32 位块)的编译器也必须支持它,而是允许编译器根据客户的需求或编译器作者认为合适的任何其他标准,是否支持此类构造,并且它允许将此类构造用于仅寻求“符合”而不是“严格符合”的程序中的程序中。

Implementations that make a bona fide effort to be maximally suitable for low-level programming tasks will support that extension by processing such constructs "in a documented fashion characteristic of the environment".真正努力以最大限度地适用于低级编程任务的实现将通过“以环境特征的文档化方式”处理此类构造来支持该扩展。 GCC is issuing a warning because it is not configured to be suitable for such usage; GCC 发出警告,因为它的配置不适合此类用途; adding the compilation flag -fno-strict-aliasing will configure it properly and thus eliminate the warning.添加编译标志-fno-strict-aliasing将正确配置它,从而消除警告。

While compilers designed for low-level programming nned not guarantee that all possible situations involving type punning will be processed in the manner implied by object representations, they should have no difficulty supporting situations where casts a pointer and immediately dereferences it as the new type for the purposes of accessing an object of the original type.虽然为低级编程设计的编译器并不能保证所有可能的涉及类型双关的情况都将以对象表示所暗示的方式进行处理,但它们应该可以毫无困难地支持强制转换指针并立即将其取消引用为新类型的情况。访问原始类型的对象的目的。 Implementations which do not seek to be maximally suitable for low-level programming, however, may require the use of clunkier constructs which, depending upon the target platform, may or may not be processed as efficiently.然而,不寻求最大程度地适合低级编程的实现可能需要使用更笨拙的构造,这取决于目标平台,可能会或可能不会被有效地处理。 When invoked without the -fno-strict-aliasing flag, the optimizers of clang and gcc fall into the latter category.当在没有-fno-strict-aliasing标志的情况下调用时,clang 和 gcc 的优化器属于后一类。

As a general principle, optimizations that assume a program won't do X may be useful in cases where there is no need to do X, but counter-productive for code whose purpose could best be described using X. If a program would benefit from being able to use low-level type punning constructs, documenting that it is only suitable for use with compiler configurations that support them is better than trying to work around their absence, especially given that clang and gcc don't reliably uphold the Standard in corner cases that would arise when trying to work around their lack of low-level programming support.作为一般原则,假设程序不会执行 X 的优化在不需要执行 X 的情况下可能很有用,但对于其目的最好使用 X 来描述的代码会适得其反。如果程序将从中受益能够使用低级类型双关构造,记录它只适用于支持它们的编译器配置比试图解决它们的缺失要好,特别是考虑到 clang 和 gcc 不能可靠地维护角落的标准试图解决缺乏低级编程支持时会出现的情况。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 警告! 取消引用类型化指针会破坏严格混叠规则[-Wstrict-aliasing] - warning! dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing] “解除引用类型惩罚指针将破坏严格别名规则”警告 - “dereferencing type-punned pointer will break strict-aliasing rules” warning C ++生成警告:取消引用类型标记的指针将破坏严格的别名规则 - C++ build warning : dereferencing type-punned pointer will break strict-aliasing rules stof()/ stoi()带有解引用类型指针的指针将破坏严格的混叠规则 - stof()/stoi() with dereferencing type-punned pointer will break strict-aliasing rules GCC警告“解除引用类型 - 惩罚指针将破坏严格别名规则” - GCC warning “dereferencing type-punned pointer will break strict-aliasing rules” GCC 7,aligned_storage和“解除引用类型惩罚指针将破坏严格别名规则” - GCC 7, aligned_storage and “dereferencing type-punned pointer will break strict-aliasing rules” 将char []强制转换为usigned int可以得到:取消引用类型标记的指针将破坏严格的别名规则 - Casting char[] to usigned int gives: dereferencing type-punned pointer will break strict-aliasing rules 取消引用类型标记的指针将破坏严格的别名规则:将字节数组转换为数字 - dereferencing type-punned pointer will break strict-aliasing rules: array of bytes to a number 这真的违反了严格的别名规则吗? - Does this really break strict-aliasing rules? c ++ 11严格别名规则是否允许通过char *,char(&)[N]访问uint64_t,甚至std :: array <char,N>&with -fstrict-aliasing -Wstrict-aliasing = 2? - Do the c++11 strict alias rules allow accessing uint64_t via char *, char(&)[N],even std::array<char, N>& with -fstrict-aliasing -Wstrict-aliasing=2?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM