将浮点二进制转换为十进制时,结果略有不准确

时间:2014-08-12 20:28:35

标签: c binary decimal

它应该做什么: 输入示例 - 101.101 输出 - 5.625

这是我写一个浮点二进制 - 十进制转换器的方式,但有 一个小错误 - 问题是输出不准确到正确的小数点。

我的代码做了什么: 输入 - 101.101 输出 - 5.624985

当我将计数从-16更改为-32时,我的代码执行了什么操作:

输入 - 101.101输出 - 5.625000这是正确的。

输入 - 101.111输出 - 5.875061这仍然是关闭它应该是5.875

#include <stdio.h>

double decimal(double decpart);
long int integer(long int intpart);

int main(int argc, const char * argv[])
{

    double x;
    scanf("%lf", &x);
    long int intpart = (long int)x;
    double decpart = x-intpart;

    double finint = integer(intpart);
    double findec = decimal(decpart);

    double finnum = findec + finint;
    printf("%lf\n",finnum);

    return 0;
}

long int integer(long int intpart)
{
    double sum = 0;
    long int a, b, p= 0;

while(intpart>0)
{
    a = intpart % 10;
    b = a*(pow(2, p));
    sum = sum + b;
    p++;
    intpart = intpart / 10;
}
    return sum;
}

double decimal(double decpart)
{
    double sum = 0;
    int count = 0;
    while (decpart > 0 && count > -32)
    {
        count--;
        decpart = decpart*10;
        if (decpart >= 1)
        {
            decpart = decpart - 1;
            sum = sum + pow(2, count);
        }
    }

    return sum;
}

5 个答案:

答案 0 :(得分:2)

不准确性是从pow函数构建的舍入错误,即使对于整数参数,它几乎总是有一个小错误。这是因为pow(x, y)通常基于数学身份exp(log(x) * y)实施,其中logexp使用自然基础2.718281828...。因此,即使在例如基数为2,log(2)是近似值,因此exp(log(2))将更接近近似值。

在您的情况下,您可以拥有一个count字段,而不是powdouble value,而0.5字段从0.5开始,并乘以double decimal(double decpart) { double sum = 0; double value = 0.5; while (decpart > 0 && value > 1.0e-5) // approx. 2 ^ -16 { decpart = decpart*10; printf("%lf\n",decpart); if (decpart > 1) { decpart = decpart - 1; sum = sum + value; } value *= 0.5; } return sum; } 每次迭代后:

pow

一般来说,这比value替代方案更准确。在符合IEEE-754标准的系统(大多数现代系统)上,scanf 应该始终是您想要的确切值。


此外,正如我/其他人所提到的,使用double将输入读作char而不是字符串导致不准确,如0.1之类的数字往往无法准确存储。相反,您应该输入{{1}}数组,然后解析字符串。

答案 1 :(得分:2)

问题是-16,这只是65,536或(0.0000153 ......)中的一部分。你得到的答案和愿望都在这个范围内。相反,需要一个更负的值,如-32或-53。 (或约ln2(DBL_EPSILON)) -

[Edit2] -17,-18等值也有其他问题。见下文。

另外if (decpart > 1) - &gt; if (decpart >= 1)


[编辑]

根据大多数 DBL_MIN_10_EXP的{​​{1}}的C规范和典型的二进制浮点数,合理的-37将提供完全 pow(2, count)的答案范围为count-80

一旦输入N位有效数字(“101.101”为6),您读取十进制数字并将其视为二进制FP编号的方法可能会中断。预计+80应为N或至少8或9位数字。要超出该限制,建议@Drew McGowen建议,并将您的输入作为字符串进行读取和处理。

[EDIT2]

鉴于典型1/DBL_EPSILONdouble有效数字的限制大约为16或17.这不仅限制了输入,而且限制了迭代次数N。比这更深入,FP转换为“101.111”(更像while (decpart > 0 && count > -16))的字符串会产生意外结果,如101.111000000000004...

  

(数学上正确101 + 1 * 1/2 + 1 * 1/4 + 1 * 1/8 + 1 * pow(2,-15)+ 1 * pow(2,-16)))... = 5.875061

所以......迭代101.111000000000001111111...超过decimal()或大约15,16次,开始产生废话。然而,迭代16次的代码仅提供65,536(0.000015 ...)中1个部分的小数精度。因此,要获得更好的答案,需要一种新的方法(如字母@Drew McGowen,灵感来自@BLUEPIXY)。

log10(1/DBL_EPSILON)

答案 2 :(得分:2)

无法准确表达,因为0.1(10)等数字是二进制的无限小数。
所以我建议你将输入转换为字符串。

#include <stdio.h>
#include <string.h>

double bstrtod(const char *bstr){
    double x = 0, one = 1.0;
    char *p = strchr(bstr, '.');
    if(p){
        char *fp = p;
        while(*++fp){
            one /= 2.0;
            if(*fp=='1')
                x += one;
        }
    } else {
        p = strchr(bstr, '\0');
    }
    one = 1.0;
    do{
        if(*--p == '1')
            x += one;
        one *= 2.0;
    }while(p!=bstr);

    return x;
}

int main(void){
    double x = bstrtod("101.101");
    printf("%f\n", x);//5.625000
    return 0;
}

答案 3 :(得分:0)

这种不准确性来自四舍五入期间的错误。即使您输入8,它实际上存储了7.99999 ...这就是问题发生的原因。

答案 4 :(得分:0)

准确的浮点二进制到十进制转换器

此代码使用字符串将二进制输入转换为十进制输出

#include <stdio.h>
#include <string.h>

double blembo(const char *blem);

int main(void)
{
    char input[50];
    gets(input);

    int t = 0, flag = 0;
    while (input[t] != '\0')  // This whole block of code invalidates "hello" or "921" or "1.1.0" inputs
    {
        if(input[t] == '0' || input[t] == '1' || input[t] == '.')
        {
            if(input[t] == '.')
            {
                if(flag != 1)
                {
                    flag = 1;
                    t++;
                    continue;
                }
                else
                {
                    printf("\nIncorrect input\n");
                    return 0;
                }
            }
            else
            {
                t++;
                continue;
            }
        }
        else
        {
            printf("\nIncorrect input\n");
            return 0;
        }
    }

    double output = blembo(input);
    printf("%lf\n", output);
    return 0;
}

double blembo (const char *blem)
{
    double x=0, one = 1.0;
    char *p , *fp;
    p = strchr(blem, '.');
    if(p)
    {
        fp = p;
        while(*++fp)
        {
            one = one / 2.0;
            if(*fp == '1')
            {
                x = x + one;
            }
        }
    }
        else
        {
            p = strchr(blem, '\0');
        }

    one = 1.0;
    do
    {
        if(*--p == '1')
        {
            x = x + one;
        }

        one = one * 2.0;
    }
    while(p!=blem);

    return x;
}