双人中有多少个sig无花果?

时间:2015-08-07 21:04:09

标签: c++ floating-point

众所周知,浮点数不能完全代表大多数数字。我不是在问浮标或双打精度的问题。

在程序中,浮点数“来自某个地方”。有些可能是通过提升整数来实现的,有些则可能是数字文字。

jQuery(document).ready(function() {
  $("div.sidemenugroup h3").hover(function() {
    $(this).next("div.sidemenugroup ul").slideDown()
  }, function(e) {
    $(this).next("div.sidemenugroup ul").slideUp();
  });
});

当然,一些浮点数来自涉及其他数字的计算。

int x = 3;
double xd = x;
float xf = 3.0f;
double xd2 = 3.0;

在我的问题中,我有时会从文本文件中读取浮点数,有时候,我会从一个复杂的函数中接收它们,我必须把它当作黑盒子。文本文件的创建者可以选择输入尽可能多的有效数字 - 他们可能只使用三个,或者可能只有八个。

然后我将使用这些数字执行一些计算,我想知道创建它们时隐含了多少重要数字。

对于参数,请考虑我正在对输入点执行自适应分段最小二乘拟合。我将继续分割我的分段,直到达到一定的容差。我希望(部分)基于输入数据的有效数字的公差 - 如果数据四舍五入到最接近的10 ^ -3,则不适合10 ^ -8。

其他人(下方)提出了类似问题(但不完全相同)。例如,我并不特别关注以漂亮的形式向用户输出表示。我并不特别关心从输出文本值中恢复相同的浮点表示。

How to calculate the number of significant decimal digits of a c++ double?

How can I test for how many significant figures a float has in C++?

我想根据双子本身的价值来计算sig figs。我不想对原始数据文件进行大量的文本处理。

在大多数情况下,浮点数最终会以大量的0000000或99999999结尾。一个直观的问题陈述是我有兴趣弄清楚重复0或9序列的开始位置。但是,我宁愿不使用循环舍入的字符串转换方法。我希望有一种相当直接和有效的方法来解决这个问题。

或许在查看最不重要的'on'位然后弄清楚它的大小时这么简单?

2 个答案:

答案 0 :(得分:0)

好的,我已经想出了类似的东西......

#include <cstdlib>
#include <cstdio>
#include <cfloat>
#include <cmath>

double sigfigs( double x )
{
    int m = floor( log10( std::abs( x ) ) );

    double pow10i;
    for ( int i = m; i > -26; i-- )
    {
        pow10i = pow( 10, i );
        double y = round( x / pow10i ) * pow10i;
        if ( std::abs( x - y ) < std::abs( x ) * 10.0 * DBL_EPSILON )
            break;
    }

    return pow10i;
}

int main( )
{
    char fmt[10];
    sprintf( fmt, "%%.%de", DBL_DIG + 3 );

    double x[9] = {1.0, 0.1, 1.2, 1.23, 1.234, 1.2345, 100.2, 103000, 100.3001};

    for ( int i = 0; i < 9; i++ )
    {
        printf( "Double: " );
        printf( fmt, x[i] );
        printf( " %f is good to %g.\n", x[i], sigfigs( x[i] ) );
    }

    for ( int i = 0; i < 9; i++ )
    {
        printf( "Double: " );
        printf( fmt, -x[i] );
        printf( " %f is good to %g.\n", -x[i], sigfigs( -x[i] ) );
    }

    exit( 0 );
}

提供输出:

Double: 1.000000000000000000e+00 1.000000 is good to 1.
Double: 1.000000000000000056e-01 0.100000 is good to 0.1.
Double: 1.199999999999999956e+00 1.200000 is good to 0.1.
Double: 1.229999999999999982e+00 1.230000 is good to 0.01.
Double: 1.233999999999999986e+00 1.234000 is good to 0.001.
Double: 1.234499999999999931e+00 1.234500 is good to 0.0001.
Double: 1.002000000000000028e+02 100.200000 is good to 0.1.
Double: 1.030000000000000000e+05 103000.000000 is good to 1000.
Double: 1.003001000000000005e+02 100.300100 is good to 0.0001.
Double: -1.000000000000000000e+00 -1.000000 is good to 1.
Double: -1.000000000000000056e-01 -0.100000 is good to 0.1.
Double: -1.199999999999999956e+00 -1.200000 is good to 0.1.
Double: -1.229999999999999982e+00 -1.230000 is good to 0.01.
Double: -1.233999999999999986e+00 -1.234000 is good to 0.001.
Double: -1.234499999999999931e+00 -1.234500 is good to 0.0001.
Double: -1.002000000000000028e+02 -100.200000 is good to 0.1.
Double: -1.030000000000000000e+05 -103000.000000 is good to 1000.
Double: -1.003001000000000005e+02 -100.300100 is good to 0.0001.

它似乎主要按预期工作。 pow(10,i)有点不幸,估计数量的基数为10级。

此外,可表示双打之间差异的估计有些粗糙。

是否有人发现任何失败的角落案例?有没有人看到任何明显的方法来改善或优化这个?如果真的很便宜会很好......

罗布

答案 1 :(得分:0)

我建议将问题分为两个步骤:

  1. 找到每个号码的最小有效数字。
  2. 分析步骤1中的数字分布。
  3. 对于步骤1,您可以使用prior answer中描述的方法,或基于转换为十进制字符串的方法,然后对字符串进行正则表达式分析。 0.1的最小位数确实是1,正如该答案中的算法所报告的那样。

    在修复第2步的规则之前,您应该检查由您知道输入有效数字的几组不同实际数字产生的分布。如果问题完全可以解决,那么在所需的位数上应该存在峰值和急剧下降。

    考虑案例(0.1 0.234 0.563 0.607 0.89)。步骤1的结果将是:

    Digits Count
    1      1
    2      1
    3      3
    

    并计数为4或更高,表示总体上有3位有效数字。