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

时间:2016-03-21 14:39:17

标签: c++ math fractions

我正在尝试计算double的小数部分中的位数,但是出现问题并且我得到无限循环:

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

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

4 个答案:

答案 0 :(得分:5)

计算机中的浮点数使用二进制(基数2,零和1,与计算机中的几乎所有数字一样)。因此,其中包含确切数量的二进制数字, BOOST_CLASS_IMPLEMENTATION(my_primitive_type, boost::serialization::primitive_type) 更多,double更少。但是,如果将像3.141592这样的十进制常量转换为float,则以完全准确的方式打印它,您将无法获得相同的数字。这是因为转换基数之间的小数一般不精确(它与1/3具有无限十进制扩展0.33的效果相似)。例如:

double

为我输出:

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

因此,当您在问题代码中开始将此乘以10时,您可以看到它在很长一段时间内不会成为精确整数(到那时它远远超出3.14159200000000016217427400988526642322540283203125 的范围),所以你的条件永远不会是真的(要么是小数部分,要么是双倍太大而不适合整数)。

换句话说,你问的是,直接从int计算数字是没有意义的。首先需要将其打印为字符串,否则您实际上 要计算多个十进制数字。当然你可以对它进行微观优化并跳过实际转换为ASCII或UTF-16字符串,但是我没有看到那种值得麻烦的情况(除非作为学习练习)。

如果在计算中需要精确的十进制数字,则需要一个特殊的数字类型,它不会以二进制形式存储分数。此类数字类型的示例为Java BigDecimal

答案 1 :(得分:2)

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 :(得分:2)

  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,双重划分如下:

    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有点不规则。有关更多信息,请参阅

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

答案 3 :(得分:0)

Atharva MR Fire在这里。

我做了一个简单的函数,可以计算双精度位数(浮点数为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;

我希望这会有所帮助。