圆形错误从double转换为字符串而不使用%f

时间:2014-08-12 08:14:36

标签: c floating-point

关注这个问题:

If you dont have %f in C, how to write a C program to print decimal number upto 2 decimal places without %f?

这是我的尝试:

#include <stdio.h>
#include <math.h>

void dbl2str(char *s, double number, int decimals)
{
    double integral, fractional;
    int n, i;

    fractional = modf(number, &integral);
    n = sprintf(s, "%d%c", (int)integral, decimals ? '.' : 0);
    for (i = 0; i < decimals; i++) {
        fractional *= 10;
        s[n + i] = '0' + (int)fractional;
        fractional = modf(fractional, &integral);
    }
    s[n + i] = '\0';
}

int main(void)
{
    char s[32];

    dbl2str(s, 3.1416, 4);
    printf("%s\n", s);
    dbl2str(s, 3.159, 4);
    printf("%s\n", s);
    dbl2str(s, 3.04, 2);
    printf("%s\n", s);
    return 0;
}

输出:

3.1415
3.1589
3.04

正如您所看到的有圆形错误,有没有办法获得正确的输出?

4 个答案:

答案 0 :(得分:1)

问题在于,0.0006并不能完全代表二进制,而是表示将完全匹配0.0005999 ......这意味着当你将乘法乘以10四次时,你得到5作为你的最后一位而不是10.您需要查看序列中的下一个数字并进行适当的舍入(在这种情况下,下一个数字将大于5)。

小心舍入,因为向上舍入最后一个数字可能会导致前一个数字向上舍入(如果最后一个数字也是9)。

答案 1 :(得分:1)

该行

s[n + i] = '0' + (int)fractional;

fractional部分截断为整数。您希望最后一位数字正确舍入,因此您必须将其视为与其他数字不同:

void dbl2str(char *s, double number, int decimals)
{
    double integral, fractional;
    int n, i;

    fractional = modf(number, &integral);
    if (fractional < 0)
        fractional = -fractional;
    n = sprintf(s, "%d%c", (int)integral, decimals ? '.' : 0);
    for (i = 0; i < decimals-1; i++) {
        fractional *= 10;
        s[n++] = '0' + (int)fractional;
        fractional = modf(fractional, &integral);
    }
    fractional *= 10;
    s[n++] = '0' + (int)(fractional+0.5f);
    fractional = modf(fractional, &integral);
    s[n] = '\0';
}

我也为负数添加了测试。没有,fractional部分就会疯狂。

答案 2 :(得分:0)

添加:

 if(i == decimals - 1)
                fractional = round(fractional);
fractional *= 10;

之后

答案 3 :(得分:0)

我会做像

这样的事情
void dbl2str(char *s, double number, int decimals)
{
    double integral, fractional;
    int n, i;

    /* use an epsilon and add the correct rounding value */
    double eps= 1e-9;
    double round = 0.5*pow(10,-decimals);

    fractional = modf(number+round+eps, &integral);
    n = sprintf(s, "%d%c", (int)integral, decimals ? '.' : 0);
    for (i = 0; i < decimals; i++) {
        fractional *= 10;
        s[n + i] = '0' + (int)fractional;
        fractional = modf(fractional, &integral);
    }
    s[n + i] = '\0';
}

这样,我添加0.5 * 10 ^ -decimals以使(int) fractional正确舍入到epsilon。

适用于您的输入:

3.1416
3.1590
3.04

当然,您可能需要根据数字调整epsilon,然后考虑您已经用尽16位精度的情况。总的来说,这很难做到。