有没有办法在C语言中对数字进行四舍五入?
我不想使用ceil和floor。还有其他替代方案吗?
当我搜索答案时,我发现了这段代码片段:
(int)(num < 0 ? (num - 0.5) : (num + 0.5))
即使 float num = 4.9,上面的代码行始终打印值为 4。
4.9 + 0.5等于5.4,除非你的编译器有严重问题,否则不可能将其舍入为4。
我刚刚确认了搜索到的代码对于4.9给出了正确的答案。
marcelo@macbookpro-1:~$ cat round.c
#include <stdio.h>
int main() {
float num = 4.9;
int n = (int)(num < 0 ? (num - 0.5) : (num + 0.5));
printf("%d\n", n);
}
marcelo@macbookpro-1:~$ make round && ./round
cc round.c -o round
5
marcelo@macbookpro-1:~$
float num = 5.0
即可完成。 - Dan Storynum
超出 int
范围时,此方法将失败。2) 它会对许多其他的 float
失败,除非它使用 double
数学运算。例如:当 num + 0.5f
四舍五入为 float
结果时,(int)(num < 0 ? (num - 0.5f) : (num + 0.5f))
有许多失败的值。int
为32位而感到困惑。 - chux - Reinstate Monica在 C 语言中,要对 float
进行四舍五入,有三个 <math.h>
函数可供使用。建议使用 rintf()
。
float nearbyintf(float x);
nearbyint
函数将其参数四舍五入为浮点格式的整数值,使用当前的舍入方向而不引发“不精确”的浮点异常。C11dr §7.12.9.3 2
或者
float rintf(float x);
rint
函数与nearbyint
函数(7.12.9.3)的区别在于如果结果与参数的值不同,rint
函数可能会引发“不精确”浮点异常。 C11dr §7.12.9.4 2
或者
float roundf(float x);
round
函数将其参数四舍五入为浮点格式中最接近的整数值,对于四舍五入的中间情况,向远离零的方向舍入,不考虑当前的舍入方向。参见C11dr §7.12.9.6 2。
示例
#include <fenv.h>
#include <math.h>
#include <stdio.h>
void rtest(const char *fname, double (*f)(double x), double x) {
printf("Clear inexact flag :%s\n", feclearexcept(FE_INEXACT) ? "Fail" : "Success");
printf("Set round to nearest mode:%s\n", fesetround(FE_TONEAREST) ? "Fail" : "Success");
double y = (*f)(x);
printf("%s(%f) --> %f\n", fname,x,y);
printf("Inexact flag :%s\n", fetestexcept(FE_INEXACT) ? "Inexact" : "Exact");
puts("");
}
int main(void) {
double x = 8.5;
rtest("nearbyint", nearbyint, x);
rtest("rint", rint, x);
rtest("round", round, x);
return 0;
}
输出
Clear inexact flag :Success
Set round to nearest mode:Success
nearbyint(8.500000) --> 8.000000
Inexact flag :Exact
Clear inexact flag :Success
Set round to nearest mode:Success
rint(8.500000) --> 8.000000
Inexact flag :Inexact
Clear inexact flag :Success
Set round to nearest mode:Success
round(8.500000) --> 9.000000
Inexact flag :Exact
OP的代码存在哪些问题?
(int)(num < 0 ? (num - 0.5) : (num + 0.5))
如果num
的值不接近int
范围,那么进行强制转换(int)
将导致未定义的行为。
当num +/- 0.5
得到一个不精确的答案时。这在此处不太可能发生,因为0.5
是一个double
类型,使得加法以比float
更高的精度发生。当num
和0.5
具有相同的精度时,将0.5
加到一个数字上可能会产生数值上的四舍五入答案。(这并不是OP帖子中的整数四舍五入。)例如:刚好小于0.5的数字应该按照OP的目标四舍五入为0,但num + 0.5
得到的是一个精确的答案,在1.0和小于1.0的最小double
之间。由于无法表示精确答案,那个总和会被四舍五入,通常会导致错误的答案。对于大数也会出现类似的情况。
根据OP所述,关于 "即使float num = 4.9
,上述行始终将值打印为4" 的困境是无法被解释的。需要提供额外的代码/信息。我怀疑OP可能使用了int num = 4.9;
。
// avoid all library calls
// Relies on UINTMAX_MAX >= FLT_MAX_CONTINUOUS_INTEGER - 1
float my_roundf(float x) {
// Test for large values of x
// All of the x values are whole numbers and need no rounding
#define FLT_MAX_CONTINUOUS_INTEGER (FLT_RADIX/FLT_EPSILON)
if (x >= FLT_MAX_CONTINUOUS_INTEGER) return x;
if (x <= -FLT_MAX_CONTINUOUS_INTEGER) return x;
// Positive numbers
// Important: _no_ precision lost in the subtraction
// This is the key improvement over OP's method
if (x > 0) {
float floor_x = (float)(uintmax_t) x;
if (x - floor_x >= 0.5) floor_x += 1.0f;
return floor_x;
}
if (x < 0) return -my_roundf(-x);
return x; // x is 0.0, -0.0 or NaN
}
测试了一下 - 有时间时会再次测试。
我不确定这是一个好主意。那段代码依赖于类型转换,而我相当肯定精确的截断是未定义的。
float result = (num - floor(num) > 0.5) ? ceil(num) : floor(num);
int n = (d - floor(d) > 0.5) ? ceil(d) : floor(d);
这行代码用于四舍五入,如果小数部分大于0.5,则向上取整,否则向下取整。谷歌的代码运行正确。其背后的思想是:当小数小于0.5时向下取整,否则向上取整。(int)
将浮点数强制转换为整型,从而舍去小数部分。如果你给一个正数加上0.5,它会被舍入到下一个整数。如果你从一个负数中减去0.5,它也会做同样的事情。
将值x舍入到精度p,其中0<p<无穷大。(例如0.25、0.5、1、2等)
float RoundTo(float x, float p)
{
float y = 1/p;
return int((x+(1/(y+y)))*y)/y;
}
float RoundUp(float x, float p)
{
float y = 1/p;
return int((x+(1/y))*y)/y;
}
float RoundDown(float x, float p)
{
float y = 1/p;
return int(x*y)/y;
}
x*(...)
超出了int
的范围,则使用int(...)
会失败。 - chux - Reinstate Monicafloat
中去除小数的“正确”方法是tuncf(x)
。 - chux - Reinstate Monicadouble trunc(double x)
适用于 double
。使用 float truncf(float x);
来处理 float()
。如果使用 float x; return double trunc(x)
返回一个 float()
,这可能会浪费计算资源。 - chux - Reinstate Monicaint round(double x)
{
return x >= 0.0 ? int(x + 0.5) : int(x - int(x-1) + 0.5) + int(x-1);
}
相比于使用 ceil 和 floor 的版本,这个版本会更快。
int(x + 0.5)
在C语言中无效。算法对于x
接近0.5但小于0.5,以及x
不在int
范围内的情况会失败。 - chux - Reinstate Monica你可以在fenv.h
中使用fesetround()
(C99中引入)。可能的参数是宏FE_DOWNWARD
,FE_TONEAREST
,FE_TOWARDZERO
和FE_UPWARD
,但请注意,并非所有这些参数都被定义 - 只有平台/实现支持的参数才会被定义。然后,您可以在math.h
中使用各种round
,rint
和nearbyint
函数(也是C99)。这样,您可以设置所需的舍入行为一次,并调用相同的函数,无论值是正数还是负数。
(例如,对于lround
,通常不需要设置舍入方向即可获得所需结果。)
只需将数字加0.5并进行类型转换即可。 然后通过将其强制转换为整数来打印它。 否则,您可以使用round()函数,其中只需将参数传递为相应的数字。
替换为
(int)`。 - Earlz