C 如何将浮点舍入到给定的小数精度

C 如何将浮点舍入到给定的小数精度,c,linux,floating-point,rounding,C,Linux,Floating Point,Rounding,对于打印输出,我需要将具有系统精度的浮点数转换为具有指定精度(例如,小数点后3位)的浮点数。fprintf函数不足以满足此要求,因为它无法对某些数字进行正确的四舍五入。我尝试过的所有其他解决方案都失败了,因为当我转换回浮点时,它们都会重新引入不需要的精度。例如: float xf_round1_f(float input, int prec) { printf("%f\t",input); int trunc = round(input * pow(10, prec));

对于打印输出,我需要将具有系统精度的浮点数转换为具有指定精度(例如,小数点后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

49980000 499800000049980000

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”被转换为float进行打印——但只要我将其转换回float,精度就会恢复。第一行和第六行说明了问题案例

当然,一定有办法解决这个问题——即使第一行结果仍然是49.975002,格式化打印也会产生预期效果,但在这种情况下,确实存在问题


有什么解决办法吗

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


如果您只需要为输出格式化一个数字,您可以使用通常的
fprintf
格式说明符来完成。如果您需要精确计算这些数字,您可以将它们缩放为可表示的值,并根据您的需要以浮点或整数算法进行运算。

编辑:您可能只关心打印的结果
printf
通常足够智能,可以对指定的位数进行适当的舍入。如果您提供
“%.3f”
格式,您可能会得到所需的内容。
如果您唯一的问题是低于所需数字的情况,您可以通过使所有内容都高于所需数字来轻松解决它。不幸的是,这增加了答案的绝对误差;即使是以前精确的结果,例如
50.000
,现在也已关闭

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

input=nextafterf(input, input*1.0001);
看看它在哪里起作用


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

定点数字。这就是以宽精度整数格式保存实际数字的地方,例如long或long。你还保留了小数位数。然后你还需要一些方法来按小数位数来缩放固定点数。还有一些转换字符串的方法


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

对于二进制浮点,这是不可避免的,抱歉。@Eric抱歉在发布后立即将其删除,因为它也没有回答问题:-)。通常在这种情况下采用这种方法,例如货币金额,是使用整数类型并将其转换为固定精度以进行显示。例如,对于货币,您可以使用32美分。一些编程语言(例如COBOL)有特定的类型来处理这个问题。根本的问题是,5不等于2,就像3不等于10一样,所以任何十进制数都是二进制的无限循环展开。@JWDN:你的问题可能被否决了,因为你还没有精确地指定问题,你没有正确使用浮点,你可能不应该使用它。如果您想保留十进制数字,可以使用其他方法进行算术运算。浮点是为数学、科学和工程计算而设计的,具有较大的动态范围和良好的数学特性。您使用它的目的与设计目的不同,因此您当然会遇到问题。你接受的答案是一个模糊的问题。我很感激,但我肯定不必求助于将每个数字转换成字符串,在小数点后计算每个数字,然后再转换回数字?我希望避免这种情况(我甚至不确定它是否有效)。@JWDN:你似乎没有抓住要点。您不能将其转换回一个数字(a
float
)并精确获得49.975。用于四舍五入的方法无关紧要。
float
的值没有精确到49.975的值。无论您在
浮点值中输入什么位,它都不会有精确的值49.975。示例中正确的值是幻觉,而不是50;它们是这样显示的,只是因为您打印的数字数量有限。老实说,我确实理解这一点。但我不是第一个遇到这种情况的人,对于许多应用程序来说,这是不可接受的——因此我仍然(也许是徒劳地)希望找到一个解决方案。@JWDN:解决方案是不使用二进制浮点数据类型。@JWDN:您还没有真正说明您的问题。如果您的问题是“我想要一个二进制浮点对象中的49.975”,那么答案是您不会得到它,因为从逻辑上讲这是不可能的。但是,如果您对更大的com进行更多解释
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