如何计算double/float C++中的位数

How to count the number of digits in double/float C++

我正在尝试计算 double 的小数部分的位数,但出了点问题,我遇到了无限循环:

double pi = 3.141592;
int counter = 0;
for (; pi != int(pi); ++counter)
    pi *= 10;
cout << counter << endl;

我刚读到这个问题,但是找不到好的解决方案。真的没有比将数字转换为字符串并计算字符更好的方法了吗?我想还有更正确的方法。

num = pi - int(pi);
while abs(num) >= 0.0000001{
    num = num * 10;
    count = count + 1;
    num = num - int(num);
}
cout<< count<< endl;

要了解更多信息,您可以阅读这篇文章C/C++ counting the number of decimals?

计算机中的浮点数使用二进制(基数 2,0 和 1,就像计算机中的几乎所有数字一样)。所以它们中有一个确切数量的二进制数字,在 double 中更多,在 float 中更少。但是,如果您将像 3.141592 这样的十进制常量转换为 double,然后以完全准确的方式打印它,您将不会得到相同的数字。这是因为转换基数之间的小数通常不是精确的(这与 1/3 具有无限小数扩展 0.33... 时得到的效果类似)。示例:

double pi = 3.141592;
std::cout << std::setprecision(100) << pi << std::endl;

我的输出:

3.14159200000000016217427400988526642322540283203125

所以当你开始在你的问题代码中将它乘以 10 时,你会发现它在很长一段时间内都没有变成一个精确的整数(到那时它已经远远超出了 int 的范围) ,所以你的条件永远不会为真(要么有小数部分,要么双精度数太大而不适合整数)。

换句话说,你问的,直接从 double 计算数字,是没有意义的。您首先需要将其打印为字符串,否则您实际上 没有 多个小数位来计算。当然,您可以对其进行微优化,并跳过实际转换为 ASCII 或 UTF-16 字符串的过程,但我看不到值得为此付出努力的场景(除了作为学习练习)。

如果您在计算中需要精确的十进制数字,则需要一种特殊的数字类型,它不以二进制形式存储分数。这种数字类型的例子是 Java BigDecimal.

  1. 粗糙的是无限循环

    当您将浮点变量与小数部分相乘时,某些数字在 FPU 算术上往往表现为无理数(由于舍入误差)。所以无论你乘以多少 10 仍然有一些小数部分...

  2. 如何代替

    您需要查看二进制中的尾数,看看最低有效位在哪里。所以:

    union
        {
        double lf;
        BYTE db[8];
        } pi;
    
    pi.lf = 3.141592;
    // here just output the BYTES to memo,screen or whatever
    mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[0])); // LSB
    mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[1]));
    mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[2]));
    mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[3]));
    mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[4]));
    mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[5]));
    mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[6]));
    mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[7])); // MSB
    

    结果是pi存储为:

    0x400921FAFC8B007A hex
    0100 0000 0000 1001 0010 0001 1111 1010 1111 1100 1000 1011 0000 0000 0111 1010 bin
    

    根据Wiki IEEE_754,double是这样划分的:

    sign     = 0 bin -> +
    exponent = 100 0000 0000 bin - 1023 dec = 1
    mantissa = 1.1001 0010 0001 1111 1010 1111 1100 1000 1011 0000 0000 0111 1010 bin
    

    请注意,尾数添加了 1.,这在数字表示中是不存在的 !!!。指数也有偏差,这就是 -1023 在那里的原因。现在,当应用指数左移时,我们得到

    pi = 11.001 0010 0001 1111 1010 1111 1100 1000 1011 0000 0000 0111 1010 bin
    

    现在我们寻找最低设置的有效位位置

    pi = 11.001 0010 0001 1111 1010 1111 1100 1000 1011 0000 0000 0111 1010 bin
                                                                         ^
    

    它的权重为 2^-50=1/2^50=8.8817841970012523233890533447266e-16=~10e-16(小数点后第 50 位),这意味着您得到了约 16 个四舍五入的十进制数 pi=3.141592 表示。正确打印后,您将看到:

    3.14159200000000016
    

    您还可以使用 n10=~log10(n2) 来估计 n10 安全地由 n2 二进制小数表示的十进制小数的数量。但是对于低 n2 有点不规则。有关详细信息,请参阅

    • How do you print the EXACT value of a floating point number?

    n2=50n10=15 非常接近 ~16 小数。这意味着如果只存在安全的十进制数字,3.14159200000000016 应该被截断为 3.141592000000000

A​​tharva 先生在这里开火。

我做了一个简单的函数,可以计算双精度数字的位数。(浮点数有 8 位,所以只需将 16 更改为 8 并进行一些其他必要的更改。)

int get_float_digits(double num)
{
    int digits=0;
    double ori=num;//storing original number
    long num2=num;
    while(num2>0)//count no of digits before floating point
    {
        digits++;
        num2=num2/10;
    }
    if(ori==0)
        digits=1;
    num=ori;
    double no_float;
    no_float=ori*(pow(10, (16-digits)));
    long long int total=(long long int)no_float;
    int no_of_digits, extrazeroes=0;
    for(int i=0; i<16; i++)
    {
        int dig;
        dig=total%10;
        total=total/10;
        if(dig!=0)
            break;
        else
            extrazeroes++;
    }
    no_of_digits=16-extrazeroes;
    return no_of_digits;
}

如果您只想得到小数位数,请在末尾添加代码 no_of_digits=no_of_digits-digits;,在 return 函数之前。

希望对您有所帮助。