分解双精度的小数部分

时间:2016-11-09 10:31:53

标签: c


我需要以单个数字分解数字的小数部分,但我需要得到最明显的表示。这是我的代码,更清楚:

#include <stdio.h>

void main(){
    double value = 0.123;
    int decim_tab[6];
    int decimal;
    int i;

    for (i = 0; i < 6; ++i) {
        value *= 10;
        decimal = (int)value;
        decim_tab[i] = decimal;
        value -= decimal;
    }
    for (i = 0; i < 6; ++i)
        print("%d\n", decim_tab[i]);
}

我需要的输出是:

1
2
3
0
0
0

但我明白了:

1
2
2
9
9
9

修改

我找到的解决方案是在值中添加一个小的delta,以强制进行最短的表示:

#include <stdio.h>

void main(){
    double value = 0.123;
    int decim_tab[6];
    int decimal;
    int i;

    value += 0.000000001
    for (i = 0; i < 6; ++i) {
        value *= 10;
        decimal = (int)value;
        decim_tab[i] = decimal;
        value -= decimal;
    }
    for (i = 0; i < 6; ++i)
        print("%d\n", decim_tab[i]);
}

我很乐意找到更好的方法,有什么建议吗?

3 个答案:

答案 0 :(得分:3)

您获得意外输出的原因是,使用(最常见的)基本两个浮点数不能始终精确地表示小数部分。在分配printf("%.20f", value);后使用value,您将看到值0.123实际存储为0.12299 ...,这就是您收到该输出的原因。

如果您只需打印六位数,则可以使用浮点数的字符串格式:

#include <stdio.h>
#include <stdlib.h>

int main(){
    double value = 0.123;
    char *s = malloc(9);

    sprintf(s++, "%.6f", value);
    while(*s++){
        putchar(*s);
        putchar('\n');
    }
}

编辑:我的回答中的代码非常具体到您给出的示例,因此在使用它时请注意我做了一些假设,例如:您的值在小数点前不会超过一位数。

答案 1 :(得分:1)

如果您想要6位小数,则应添加0.0000005(即0.5e-6)以将值四舍五入到最近的位置。此方法适用于正数,首先提取符号,然后处理绝对值。

答案 2 :(得分:1)

浮点数不是精确值表示。这是一个简单的示例:

double a = 0.15 + 0.15; // 0.15 + 0.15 == 0.3, right?
double b = 0.1 + 0.2;   // 0.1 + 0.2 == 0.3, right?
if (a == b) {
  printf("Equal\n");
} else {
  printf("Unequal\n");
}

那会打印什么? Equal?你确定吗?亲自尝试一下:

http://rextester.com/VZOZ1043

它打印Unequal,这是因为有一些浮点数无法准确表示的数字,而且在进行浮点数学运算时总是需要记住这些数字。在许多操作中也涉及舍入,因此数学运算的结果尽可能好但不总是“精确”,如果你运行多个操作,也会有一个很小的错误。

double value = 0.123;

// Assuming none of your numbers has more than 58 digits,
// one period and one termination char.
char buffer[60];

// Print the number to the buffer.
// Missing: Error checking if it did fit!
snprintf(buffer, sizeof(buffer), "%f", value);

// Find the period or end of string
int idx = 0;
for (; buffer[idx] && buffer[idx] != '.'; idx++);

// Print anything after the period till 
// the end of the string
if (buffer[idx] == '.') {
    for (idx++; buffer[idx]; idx++) {
        printf("%c\n", buffer[idx]);
    }
}

测试它:http://rextester.com/CYDQO24769