可以std :: numeric_limits :: quiet_NaN double/float存储一些额外的信息 [英] Can std::numeric_limits::quiet_NaN double/float store some extra info

查看:835
本文介绍了可以std :: numeric_limits :: quiet_NaN double/float存储一些额外的信息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的数据采集项目中存储双精度数据时,我使用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 of NaN).

请注意,此实现的效果不佳,因为从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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆