-2

我需要将具有系统精度的浮点数转换为具有指定精度(例如 3 个小数位)的浮点数以用于打印输出。fprintf 函数不足以解决此问题,因为它无法正确舍入某些数字。我尝试过的所有其他解决方案都失败了,因为当我转换回浮点数时,它们都重新引入了不希望的精度。例如:

float xf_round1_f(float input, int prec) {

    printf("%f\t",input);
    int trunc = round(input * pow(10, prec));
    printf("%f\t",(float)trunc);
    input=(float)trunc / pow(10, prec);
    printf("%f\n",input);
    return (input);

}

此函数将输入、截断的整数和输出打印到每一行,对于一些应该截断到小数点后 3 位的数字,结果如下所示:

49.975002 49975.000000 49.974998

49.980000 49980.000000 49.980000

49.985001 49985.000000 49.985001

49.990002 49990.000000 49.990002

49.995003 49995.000000 49.994999

50.000000 50000.000000 50.000000

您可以看到第二步按预期工作 - 即使将“trunc”强制转换为浮点数以进行打印 - 但一旦我将其转换回浮点数,精度就会返回。第 1 行和第 6 行说明了问题案例。

当然必须有一种方法来解决这个问题——即使第一行结果仍然是 49.975002,格式化的打印也会产生预期的效果,但在这种情况下存在一个真正的问题。

有什么解决办法吗?

4

4 回答 4

4

二进制浮点不能准确表示大多数十进制数字。每个二进制浮点数由整数乘以 2 的幂形成。float对于IEEE-754 32 位二进制浮点的常见实现,该整数必须在 (–2 24 , 2 24 ) 中。没有整数x和整数y使得x •2 y完全等于 49.975。因此,当您将 49975 除以 1000 时,结果必须是近似值。

如果您只需要格式化输出的数字,您可以使用通常的fprintf格式说明符来执行此操作。如果您需要精确计算这些数字,您可以通过将它们缩放到可表示的值并根据您的需要以浮点或整数算术进行算术来实现。

于 2013-10-02T19:11:44.717 回答
1

编辑:看来您可能只关心打印的结果。printf通常足够聪明,可以对您指定的位数进行适当的舍入。如果你给出一个格式,"%.3f"你可能会得到你需要的东西。


如果您唯一的问题是低于所需数字的案例,您可以通过使所有内容高于所需数字来轻松解决它。不幸的是,这增加了答案的绝对误差;甚至是以前精确的结果,例如50.000现在关闭。

只需将此行添加到函数的末尾:

input=nextafterf(input, input*1.0001);

在http://ideone.com/iHNTzs上查看它的实际应用

49.975002   49975.000000    49.974998   49.975002
49.980000   49980.000000    49.980000   49.980003
49.985001   49985.000000    49.985001   49.985004
49.990002   49990.000000    49.990002   49.990005
49.995003   49995.000000    49.994999   49.995003
50.000000   50000.000000    50.000000   50.000004
于 2013-10-02T19:28:53.777 回答
1

如果您需要用小数点后三位精确表示所有小数,您可以使用千分之一。使用整数数据类型来表示所有中间结果的实际数字的一千倍。

于 2013-10-02T19:29:12.523 回答
0

定点数。这就是您将实际数字保存为宽精度整数格式的地方,例如 long 或 long long。而且您还保留小数位数。然后您还需要按小数位缩放定点数的方法。以及某种与字符串相互转换的方法。

您遇到问题的原因是 1/10 不能完全表示为 2 的分数幂(1/2、1/4、1/8 等)。这与 1/3 是以 10 为底的重复小数(0.33333...)的原因相同。

于 2013-10-03T07:48:23.957 回答