如何将浮点数舍入到C中最接近的整数?

时间:2010-04-03 10:23:03

标签: c rounding

有没有办法在C中舍入数字?

我不想使用ceil和floor。还有其他选择吗?

当我用Google搜索答案时,我遇到了这段代码:

(int)(num < 0 ? (num - 0.5) : (num + 0.5))

即使浮点数= 4.9,上面的行总是将值打印为4。

11 个答案:

答案 0 :(得分:9)

4.9 + 0.5是5.4,除非你的编译器被严重破坏,否则它不可能舍入到4。

我刚刚确认Google搜索代码为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:~$

答案 1 :(得分:3)

一般解决方案是使用rint()并根据需要设置FLT_ROUNDS舍入模式。

答案 2 :(得分:3)

我不确定这是个好主意。该代码依赖于强制转换,我很确定精确的截断是未定义的。

float result = (num - floor(num) > 0.5) ? ceil(num) : floor(num);

我会说这是一个更好的方式(这基本上是Shiroko发布的),因为它不依赖于任何演员阵容。

答案 3 :(得分:3)

要在C中舍入float,有3个<math.h>函数可以满足需要。建议rintf()

float nearbyintf(float x);
  

nearbyint函数将它们的参数围绕浮点格式的整数值,使用当前的舍入方向并且不引发“不精确的”浮点异常。 C11dr§7.12.9.32

float rintf(float x);
  

rint函数与nearbyint函数(7.12.9.3)的不同之处仅在于,如果结果不同,rint函数可能会引发''不精确''浮点异常来自论证的价值。 C11dr§7.12.9.42

float roundf(float x);
  

round函数将它们的参数四舍五入为浮点格式的最接近的整数值,无论当前的舍入方向如何,都将中间情况四舍五入。 C11dr§7.12.9.62

实施例

#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))
  1. 如果num的值不在int范围内,则广告(int)会导致未定义的行为。

  2. num +/- 0.5导致答案不准确时。这不太可能,因为0.5double,导致添加的精度高于float。当num0.5具有相同的精度时,将0.5添加到数字可能会导致数字舍入答案。 (这不是OP职位的整数舍入。)示例:小于0.5的数字应该按照每个OP的目标舍入为0,但是num + 0.5会导致1.0和最小double之间的确切答案不到1.0。由于确切的答案是不可表示的,因此总和通常为1.0,导致答案不正确。大量的情况也会发生类似的情况。

  3. 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
    }
    

    经过测试 - 稍后我会有时间这样做。

答案 4 :(得分:1)

我认为你在寻找的是: int n = (d - floor(d) > 0.5) ? ceil(d) : floor(d);

答案 5 :(得分:1)

Google搜索代码正常运行。它背后的想法是,当小数小于0.5时向下舍入,否则向上舍入。 (int)将float转换为一个只删除小数的int类型。如果将.5添加到正数,则会转到下一个int。如果你从负数中减去.5就会做同样的事情。

答案 6 :(得分:0)

您可以在fesetround()中使用fenv.h(在C99中引入)。可能的参数是宏FE_DOWNWARDFE_TONEARESTFE_TOWARDZEROFE_UPWARD,但请注意,并非所有宏都必须定义 - 只有平台/实现支持的那些是。然后,您可以使用round(也是C99)中的各种rintnearbyintmath.h函数。这样,您可以设置一次所需的舍入行为,并调用相同的函数,无论该值是正还是负。

(例如lround你通常不需要设置正常使用的舍入方向来获得你想要的东西。)

答案 7 :(得分:0)

int round(double x)
{
return x >= 0.0 ? int(x + 0.5) : int(x - int(x-1) + 0.5) + int(x-1);
}

它会比使用ceil和floor的版本更快。

答案 8 :(得分:0)

舍入值x到精度p,其中0 < p&lt;无穷。 (f.ex.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;
}

答案 9 :(得分:0)

只需在数字上加上0.5并键入。 并通过将其类型转换为整数进行打印。 否则,您可以使用round(),在其中只需将参数作为相应的数字传递。

答案 10 :(得分:-4)

尝试移动上方解决方案中的括号,使其显示为:

(int)(num < 0) ? (num - 0.5) : (num + 0.5)

使用num作为4.9,它在我的机器上舍入为5。