如何将浮点数舍入到C中最接近的整数?
有没有办法在C中舍入数字?
我不想使用ceil和地板。 还有其他选择吗?
当我用Google搜索答案时,我遇到了这段代码:
(int)(num < 0 ? (num - 0.5) : (num + 0.5))
即使float num = 4.9,上面的行总是将值打印为4。
4.9 + 0.5是5.4,除非编译器严重损坏,否则不能四舍五入到4。
我刚刚确认Google搜索代码给出了4.9的正确答案。
marcelo@macbookpro-1:~$ cat round.c #include 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:~$
一般的解决方案是使用rint()并根据需要设置FLT_ROUNDS舍入模式。
我不确定这是个好主意。 该代码依赖于强制转换,我很确定精确的截断是未定义的。
float result = (num - floor(num) > 0.5) ? ceil(num) : floor(num);
我会说这是一个更好的方式(这基本上是Shiroko发布的),因为它不依赖于任何演员阵容。
我想你要找的是: int n = (d - floor(d) > 0.5) ? ceil(d) : floor(d);
int n = (d - floor(d) > 0.5) ? ceil(d) : floor(d);
谷歌搜索的代码正常工作。 它背后的想法是,当小数小于.5时向下舍入,否则向上舍入。 (int)
将float转换为一个只删除小数的int类型。 如果将.5添加到正数,则会转到下一个int。 如果你从负数中减去.5就会做同样的事情。
要在C中舍入float
,有3个
函数可以满足需要。 推荐rintf()
。
float nearbyintf(float x);
nearbyint
函数将其参数围绕浮点格式的整数值,使用当前的舍入方向,而不会引发“不精确”的浮点exception。 C11dr§7.12.9.32
要么
float rintf(float x);
rint
函数与nearbyint函数(7.12.9.3)的不同之处仅在于,如果结果与参数的值不同,则rint
函数可能会引发”不精确”浮点exception。 C11dr§7.12.9.42
要么
float roundf(float x);
round
函数将它们的参数四舍五入为浮点格式的最接近的整数值,无论当前的舍入方向如何,都将中间情况舍入为零。 C11dr§7.12.9.62
例
#include #include #include 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
范围内,则cast(int)
导致未定义的行为。 -
当
num +/- 0.5
导致不准确的答案时。 这不太可能,因为0.5
是double
导致加法以比float
更高的精度发生。 当num
和0.5
具有相同的精度时,将0.5
添加到数字可能会导致数字舍入答案。 (这不是OP的post的整数舍入。)例如:小于0.5的数字应该是每个OP的目标为0,但是num + 0.5
导致1.0和最小的double
精度之间的精确答案,小于1.0。 由于确切的答案是不可表示的,因此总和通常为1.0,导致答案不正确。 类似的情况发生在大数字上。
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; // 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 }
经过测试很少 - 我有时间会这样做。
您可以在fenv.h
使用fesetround()
(在C99中引入)。 可能的参数是宏FE_DOWNWARD
, FE_TONEAREST
, FE_TOWARDZERO
和FE_UPWARD
但请注意,并非所有参数都必须定义 – 只有平台/实现支持的参数。 然后你可以在math.h
(也是C99)中使用各种round
, rint
和nearbyint
函数。 这样,您可以设置一次所需的舍入行为,并调用相同的函数,无论该值是正还是负。
(例如lround
你通常不需要设置正常使用的舍入方向,通常可以得到你想要的。)
int round(double x) { return x >= 0.0 ? int(x + 0.5) : int(x - int(x-1) + 0.5) + int(x-1); }
它会比带有ceil和floor的版本更快。
舍入值x到精度p,其中0
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; }
只需将0.5添加到数字并对其进行类型转换..然后通过类型将其打印为整数来打印它。否则你可以使用round()在其中传递参数作为相应的数字。
尝试移动上面解决方案中的括号,使其显示为:
(int)(num < 0) ? (num - 0.5) : (num + 0.5)
使用num作为4.9,它在我的机器上舍入为5。