浮点64位问题fir去引用类型punned指针将打破严格的混叠规则[-Wstrict aliasing]



我正在尝试下面的代码来获取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); 

[-Wstrict-aliasing]选项是-O2优化的一部分,我不必禁用它。

解决此问题的方法是什么?

这个宏是我从GCC数学库中获取的。

如果你想要浮点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;
}

我不认为这是你想要做的。如果你想要二重的分数部分和整个部分,你需要尝试其他东西。

我不确定你想实现什么,就像迪伦提到的一样。

如果你想拆开一个double(假设是IEEE 754 double(,你可以这样做(我使用的是ANSI转义序列,这可能不适合你的环境(。我有两个";将其拆分为十六进制字节";以及";分解成符号/指数/分数":

#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:33[0;32m" << bits(s, 1) << "33[0m ";
if (s) ss << "(-) ";
else ss << "(+) ";
ss << "exp:33[0;33m" << bits(e, 11) << "33[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 << "33[0;34mi";
else ss << "33[0;37m.33[34m";
ss << bits(f, 52) << "33[0m";
if (dx == FP_INFINITE) ss << " 33[35mInfinite33[0m";
else if (dx == FP_NAN) ss << " 33[35mNot-A-Number33[0m";
else if (dx == FP_NORMAL) ss << " 33[35mNormal33[0m";
else if (dx == FP_SUBNORMAL) ss << " 33[35mDenormalized33[0m";
else if (dx == FP_ZERO) ss << " 33[35mZero33[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;
}
}

投射指针并使用新投射的指针执行类型punning的能力"以环境特征的记录方式";是标准委员会所称的";流行的扩展";[见基本原理第11页]。本标准有意避免要求即使不适合低级编程任务(如将64位浮点值直接分解为32位块(的编译器也必须支持它,而是允许编译器根据客户的需求或编译器编写人员认为合适的任何其他标准来支持或不支持这种结构,并且它允许在程序中使用这样的构造,这些程序仅仅寻求成为"程序";符合";而不是";严格符合";。

真正努力最大限度地适合低级编程任务的实现将通过处理这样的构造来支持这种扩展";以环境特征的记录方式";。GCC发出警告,因为它没有被配置为适合这种用途;添加编译标志CCD_ 4将正确地配置它,从而消除警告。

虽然为低级编程设计的编译器不能保证所有可能涉及类型双关的情况都会以对象表示所暗示的方式进行处理,但它们应该很容易支持强制转换指针并立即将其取消引用为新类型以访问原始类型的对象的情况。然而,不寻求最大限度地适合于低级编程的实现可能需要使用更笨重的构造,根据目标平台的不同,这些构造可能被有效地处理,也可能不被有效地进行处理。当在没有-fno-strict-aliasing标志的情况下调用时,clang和gcc的优化器属于后一类。

作为一个一般原则,假设程序不会执行X的优化在不需要执行X的情况下可能是有用的,但对于使用X来描述其目的的代码来说却是适得其反的。如果程序能够从使用低级类型双关结构中受益,记录它只适合与支持它们的编译器配置一起使用,比试图解决它们的缺失要好,特别是考虑到clang和gcc在试图解决它们缺乏低级编程支持的情况下无法可靠地维护标准。

最新更新