将双精度浮点数的小数部分分解

4
我需要将一个数字的小数部分分解为个位数,但我需要得到最明显的表示方式。以下是我的代码,为了更清晰:
#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

编辑

我找到的解决方案是为值添加一个小偏移量,以强制使用最短的表示方式:

#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]);
}

我很愿意寻找更好的方法,您有什么建议吗?

5
相关问题:浮点数运算是否有问题? - fpg1503
3
使用 sprintf() 然后分析生成的字符串怎么样? - Roman Hocke
4
你的 value += 0.000000001 方案并不可行,它会破坏其他本来不会出现问题的数字。你的问题在于将十进制浮点文字转换为二进制浮点数时,从来都无法完全精确地转换。如果你需要完美的分解,就不能使用浮点数类型。 - user694733
3个回答

3
你之所以会得到意外的输出,是因为十进制小数不能总是使用(最常见的)二进制浮点数准确地表示。在你赋值给value后使用printf("%.20f", 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
如果您想保留六位小数,应添加0.0000005(即0.5e-6),以将值四舍五入到最近的位置。该方法适用于正数,首先提取符号,然后处理绝对值。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接