以下是我要实现的目标:
我想这些问题包含两部分:
1)我可以简单地清除尾数的最低位吗?我试过这个,到目前为止它的确有效,但也许我在寻找麻烦......有点像:
float f;
int packed = (*(int*)&f) & ~3;
// later
f = *(float*)&packed;
2)如果有1)失败的情况,那么实现这一目标的最快方法是什么?
提前致谢
答案 0 :(得分:10)
您实际上违反了严格的别名规则(C ++标准的第3.10节)以及这些重新解释转换。当你打开编译器优化时,这可能会在你脸上爆炸。
C ++标准,第3.10节第15段说:
如果程序试图通过以下类型之一以外的左值访问对象的存储值,则行为未定义
- 对象的动态类型,
- 对象的动态类型的cv限定版本,
- 类似于对象的动态类型的类型
- 与对象的动态类型对应的有符号或无符号类型的类型
- 与对象的动态类型的cv限定版本对应的有符号或无符号类型的类型,
- 聚合或联合类型,其成员中包含上述类型之一(包括递归地,子聚合或包含联合的成员),
- 一种类型,它是对象动态类型的(可能是cv限定的)基类类型,
- char或unsigned char类型。
具体来说,3.10 / 15不允许我们通过unsigned int类型的左值访问float对象。实际上我被这个咬了。我写的程序在启用优化后停止了工作。显然,GCC没想到float类型的左值是别名类型为int的左值,这是3.10 / 15的公平假设。在利用3.10 / 15的as-if规则下,优化器对指令进行了改组,并且它停止了工作。
根据以下假设
你应该能够这样做:
/// returns a 30 bit number
unsigned int pack_float(float x) {
unsigned r;
std::memcpy(&r,&x,sizeof r);
return r >> 2;
}
float unpack_float(unsigned int x) {
x <<= 2;
float r;
std::memcpy(&r,&x,sizeof r);
return r;
}
这不受“3.10违规”的影响,通常非常快。至少GCC将memcpy视为内在函数。如果您不需要使用NaN,无穷大或具有极高幅度的数字的函数,您甚至可以通过将“r&gt;&gt; 2”替换为“(r + 1)&gt;&gt; 2”来提高准确性:
unsigned int pack_float(float x) {
unsigned r;
std::memcpy(&r,&x,sizeof r);
return (r+1) >> 2;
}
即使由于尾数溢出而改变指数也是有效的,因为IEEE-754编码将连续的浮点值映射到连续的整数(忽略+/-零)。这种映射实际上非常接近对数。
答案 1 :(得分:8)
对于少量不寻常的NaN编码,盲目地丢弃浮点数的2个LSB可能会失败。
NaN编码为指数= 255,尾数!= 0,但IEEE-754没有说明应该使用哪个mantiassa值。如果尾数值<= 3,则可以将NaN变为无穷大!
答案 2 :(得分:2)
你应该将它封装在一个结构中,这样你就不会意外地将标记浮点数的用法与常规的“unsigned int”混合使用:
#include <iostream>
using namespace std;
struct TypedFloat {
private:
union {
unsigned int raw : 32;
struct {
unsigned int num : 30;
unsigned int type : 2;
};
};
public:
TypedFloat(unsigned int type=0) : num(0), type(type) {}
operator float() const {
unsigned int tmp = num << 2;
return reinterpret_cast<float&>(tmp);
}
void operator=(float newnum) {
num = reinterpret_cast<int&>(newnum) >> 2;
}
unsigned int getType() const {
return type;
}
void setType(unsigned int type) {
this->type = type;
}
};
int main() {
const unsigned int TYPE_A = 1;
TypedFloat a(TYPE_A);
a = 3.4;
cout << a + 5.4 << endl;
float b = a;
cout << a << endl;
cout << b << endl;
cout << a.getType() << endl;
return 0;
}
我不能保证它的便携性。
答案 3 :(得分:1)
我不能选择任何答案作为明确的答案,因为他们中的大多数都有有效的信息,但不是我想要的。所以我只想总结一下我的结论。
我在问题的第1部分中发布的转换方法显然是错误的C ++标准,因此应该使用其他提取浮点数的方法。
最重要的......据我所知,通过阅读有关IEEE754浮点数的响应和其他来源,可以从尾数中删除最低有效位。它主要影响精度,但有一个例外:sNaN。由于sNaN由设置为255的指数表示,并且尾数!= 0,因此可能存在尾数将<= 3的情况,并且丢弃最后两位将将sNaN转换为+/-无穷大。但由于sNaN不是在CPU上的浮点运算期间生成的,因此在受控环境下是安全的。
答案 4 :(得分:1)
您需要多少精度?如果16位浮点数足够(足以用于某些类型的图形),那么ILM的16位浮点数(“一半”),OpenEXR的一部分很棒,遵守各种规则(http://www.openexr.com/ ),将它打包成结构后,你将有足够的空间。
另一方面,如果您知道它们将要采用的大致值范围,您应该考虑固定点。它们比大多数人意识到的更有用。