可以std :: numeric_limits :: quiet_NaN double/float存储一些额外的信息 [英] Can std::numeric_limits::quiet_NaN double/float store some extra info
问题描述
在我的数据采集项目中存储双精度数据时,我使用std::numeric_limits::quiet_NaN()
标识所有丢失"的数据.但是,我想存储一些额外的信息,以了解为什么数据丢失"(数据传输丢失,校验和不正确,未完成测量,内部错误……),因此我需要在其中输入许多不同的"nan"值结尾.并且必须通过任何旧版代码(x!=x
)将它们全部识别为NaN.
When storing double data in my data acquisition project, I identify all "missing" data using std::numeric_limits::quiet_NaN()
. However, I'd like to store some extra information to know why the data is "missing" (data transmission lost, bad checksum, no measurement done, internal error....) so I need many different "nan" values in the end. And they must all be identified as NaN by any legacy code (x!=x
).
我在 IEEE 754-1985 中看到NaN分数可能是除全0位以外的所有内容(因为全0位表示无穷大).". fraction
可以用来安全地存储一些额外的信息吗?如果是,我应该怎么做?在所有平台上以及任何编译器下,这都是完全安全的吗?
I see in IEEE 754-1985 that NaN fraction could be "anything except all 0 bits (since all 0 bits represents infinity).". Can the fraction
be used to safely store some extra info? If yes, how should I do this? Would this be totally safe on all platform and with any compiler?
这是我在想什么:
double GetMyNaN1()
{
double value = std::numeric_limits<double>::quiet_NaN();
// customize it!
return value;
}
double GetMyNaN2()
{
double value = std::numeric_limits<double>::quiet_NaN();
// customize it!
return value;
}
bool IsMyNan1( double value )
{
// return true if value was created by GetMyNaN1()
}
bool IsMyNan2( double value )
{
// return true if value was created by GetMyNaN2()
}
int main()
{
double regular_nan = std::numeric_limits<double>::quiet_NaN();
double my_nan_1 = GetMyNaN1();
double my_nan_2 = GetMyNaN2();
assert( std::isnan( regular_nan ) && !IsMyNan1( regular_nan ) && !IsMyNan2( regular_nan ) );
assert( std::isnan( my_nan_1 ) && IsMyNan1( my_nan_1 ) && !IsMyNan2( my_nan_1 ) );
assert( std::isnan( my_nan_2 ) && !IsMyNan1( my_nan_2 ) && IsMyNan2( my_nan_2 ) );
return 0;
}
该代码必须在所有平台上均可使用.
The code must work on all platform.
推荐答案
根据戴维斯(Davis)的要求,使用NaN-boxing,我可以在Windows(MSVC)和Linux(gcc)下运行的代码轻松实现这一目标.足够满足我的需求.
Using NaN-boxing as recommanded by Davis, I could easily implement this with code working under Windows (MSVC) and Linux (gcc). Which is good enough for my needs.
#include <iostream>
#include <assert.h>
#include <limits>
#include <bitset>
#include <cmath>
void showValue( double val, const std::string& what )
{
union udouble {
double d;
unsigned long long u;
};
udouble ud;
ud.d = val;
std::bitset<sizeof(double) * 8> b(ud.u);
std::cout << val << " (" << what << "): " << b.to_string() << std::endl;
}
double customizeNaN( double value, char mask )
{
double res = value;
char* ptr = (char*) &res;
assert( ptr[0] == 0 );
ptr[0] |= mask;
return res;
}
bool isCustomNaN( double value, char mask )
{
char* ptr = (char*) &value;
return ptr[0] == mask;
}
int main(int argc, char *argv[])
{
double regular_nan = std::numeric_limits<double>::quiet_NaN();
double myNaN1 = customizeNaN( regular_nan, 0x01 );
double myNaN2 = customizeNaN( regular_nan, 0x02 );
showValue( regular_nan, "regular" );
showValue( myNaN1, "custom 1" );
showValue( myNaN2, "custom 2" );
assert( std::isnan(regular_nan) );
assert( std::isnan(myNaN1) );
assert( std::isnan(myNaN2) );
assert( !isCustomNaN(regular_nan,0x01) );
assert( isCustomNaN(myNaN1,0x01) );
assert( !isCustomNaN(myNaN2,0x01) );
assert( !isCustomNaN(regular_nan,0x02) );
assert( !isCustomNaN(myNaN1,0x02) );
assert( isCustomNaN(myNaN2,0x02) );
return 0;
}
此代码假定quiet_NaN始终为:
0111111111111000000000000000000000000000000000000000000000000000
:
0
,将11位设置为1
,然后1000000000000000000000000000000000000000000000000000
This code assumes quiet_NaN is always:
0111111111111000000000000000000000000000000000000000000000000000
:
0
, 11 bits set to 1
,then 1000000000000000000000000000000000000000000000000000
该代码可适用于:
- 通过模板实现同时支持
float
/double
- 支持大/小耐力(决定应在何处使用面罩)
- 支持任何nan表示形式(假设我的后8位是
0
,并且可以用作掩码,IEEE 754-1985可以用不同的方式表示nan,例如:0111111111110000000000000000000000000000000000000000000000000001
,然后使用后8位作为一个面具将是一个坏主意).但是,总会有一种自定义分数的方法,因为只要不将所有位都设置为0(那么它将表示+Inf
而不是NaN
),它就始终被视为NaN.
- Support both
float
/double
through a template implementation - Support big/little endianess (to decide where the mask should be applied)
- Support any nan representation (with my assumption last 8 bits are
0
and can be used as a mask, IEEE 754-1985 makes it possible to represent nan differently, for instance:0111111111110000000000000000000000000000000000000000000000000001
, then using last 8 bits as a mask would be a bad idea). But there will always be a way to customize the fraction as it will always be considered as a NaN as far as you don't end up with all bits set to 0 (which would then represent+Inf
instead ofNaN
).
请注意,此实现的效果不佳,因为从float转换为double时扩展信息会丢失.请参阅我的答案对
Note that this implementation is not that good as the extended info is lost when casting from float to double. See my answer to std::num_put issue with nan-boxing due to auto-cast from float to double for anoother implementation that is safer.
这篇关于可以std :: numeric_limits :: quiet_NaN double/float存储一些额外的信息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!