在C中实现round()的简洁方法?

fre*_*set 14 c math floating-point

我使用的嵌入式C没有round()函数它是数学库,在C中实现它的简洁方法是什么?我想把它打印成一个字符串,查找小数位,然后在句点后找到第一个字符,然后在> = 5时向上舍入,否则向下.想知道是否有更聪明的东西.

谢谢,弗雷德

Bro*_*ses 18

正如许多其他答案所暗示的那样,你可以重新发明轮子.或者,您可以使用别人的方向盘 - 我建议使用Newlib,这是BSD许可的,旨在用于嵌入式系统.它正确处理负数,NaN,无穷大和不能表示为整数的情况(由于太大),以及使用指数和屏蔽而不是通常成本较高的浮点运算的有效方式.此外,它经常测试,所以你知道它没有明显的角落案例错误.

Newlib源可能有点难以导航,所以这里是您想要的位:

Float版本:https://sourceware.org/git/gitweb.cgi? p = newlib-cygwin.git; a = blob; f = newlib/libm/common/sys_round.c; hb = master

双版本:https: //sourceware.org/git/gitweb.cgi?p = newlib-cygwin.git; a = blob; f = newlib/libm/common/s_round.c; hb = master

这里定义的字提取宏:https://sourceware.org/git/gitweb.cgi?p = newlib-cygwin.git; a = blob; f = newlib/libm/common/fdlibm.h; hb = master

如果你需要其他文件,那么父目录是这样的:https: //sourceware.org/git/gitweb.cgi?p = newlib-cygwin.git; a = tree; f = newlib/libm/common; hb =主

为了记录,这是浮动版本的代码.如您所见,正确处理所有可能的情况需要一些复杂性.

float roundf(x)
{
  int signbit;
  __uint32_t w;
  /* Most significant word, least significant word. */
  int exponent_less_127;

  GET_FLOAT_WORD(w, x);

  /* Extract sign bit. */
  signbit = w & 0x80000000;

  /* Extract exponent field. */
  exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;

  if (exponent_less_127 < 23)
    {
      if (exponent_less_127 < 0)
        {
          w &= 0x80000000;
          if (exponent_less_127 == -1)
            /* Result is +1.0 or -1.0. */
            w |= ((__uint32_t)127 << 23);
        }
      else
        {
          unsigned int exponent_mask = 0x007fffff >> exponent_less_127;
          if ((w & exponent_mask) == 0)
            /* x has an integral value. */
            return x;

          w += 0x00400000 >> exponent_less_127;
          w &= ~exponent_mask;
        }
    }
  else
    {
      if (exponent_less_127 == 128)
        /* x is NaN or infinite. */
        return x + x;
      else
        return x;
    }
  SET_FLOAT_WORD(x, w);
  return x;
}
Run Code Online (Sandbox Code Playgroud)


dan*_*n04 13

int round(double x)
{
    if (x < 0.0)
        return (int)(x - 0.5);
    else
        return (int)(x + 0.5);
}
Run Code Online (Sandbox Code Playgroud)

  • 另请注意,此实现对于整数可表示的值范围之外的"x"具有未定义的行为. (9认同)
  • 请尝试上面的代码0.49999999999999994; 对于使用64位`double`进行计算的实现,将0.5添加到该值将产生1.0,即使它应该向下舍入. (5认同)
  • C库函数`round()`返回一个double,而不是int. (2认同)
  • @Daniel:如何指定`round`?规格是否允许一个绝对低于0.5的值来舍入到1.0? (2认同)

nmi*_*els 9

int round(float x)
{
    return (int)(x + 0.5);
}
Run Code Online (Sandbox Code Playgroud)

警告:仅适用于正数.

  • @Niki:我不会忘记他们; 我只是选择忽略它们.添加对它们的支持是微不足道的(参见dan04的答案),我的目标是尽量简化一种方法,而不是OP所描述的那种方法. (5认同)
  • 您忘记了负数:在您的情况下舍入(-1.3)= 0. (2认同)
  • 由于转换将向零舍入,这仅在x为正时才有效. (2认同)
  • C库函数round()返回一个double,而不是int. (2认同)
  • 参数应该是`double x`而不是`float x`,浮点数将是`float roundf(float x)`.如果`x`在`[INT_MIN,INT_MAX]`间隔之外,我认为你也会得到未定义的行为. (2认同)

mu *_*ort 7

IEEE 754推荐"圆半到均匀"方法:如果小数部分d为0.5,则舍入到最接近的偶数整数.问题是在相同方向上舍入0.5的小数部分会在结果中引入偏差; 所以,你要圆一个分数0.5了一半的时间,下半个月的时间,因而有"舍入到最近的偶数"位,四舍五入到最接近的奇数也将工作作为将翻转公平的硬币,以确定哪种方式走.

我觉得更像这样的东西是IEEE纠正的:

#include <math.h>

int is_even(double d) {
    double int_part;
    modf(d / 2.0, &int_part);
    return 2.0 * int_part == d;
}

double round_ieee_754(double d) {
    double i = floor(d);
    d -= i;
    if(d < 0.5)
        return i;
    if(d > 0.5)
        return i + 1.0;
    if(is_even(i))
        return i;
    return i + 1.0;
}
Run Code Online (Sandbox Code Playgroud)

而这个应该是C99-ish(它似乎指明0.5的小数部分的数字应该从零开始舍入):

#include <math.h>
double round_c99(double x) {
    return (x >= 0.0) ? floor(x + 0.5) : ceil(x - 0.5);
}
Run Code Online (Sandbox Code Playgroud)

而我的第一个更紧凑的版本round_c99(),这个更好地处理跨越56位尾数边界不依赖x+0.5或是x-0.5明智的事情:

#include <math.h>
double round_c99(double d) {
    double int_part, frac_part;
    frac_part = modf(d, &int_part);
    if(fabs(frac_part) < 0.5)
        return int_part;
    return int_part > 0.0 ? int_part + 1.0 : int_part - 1.0;
}
Run Code Online (Sandbox Code Playgroud)

如果|int_part| >> 1用大指数舍入双精度是没有意义的话,这会有问题.我确信三者中都有NaN,但我的受虐狂有限制,数字编程确实不是我的事.

浮点计算有足够的空间容易出现细微错误,因此简洁可能不是最佳要求.

一个更好的解决方案是大致打击你的编译器供应商的面部和颈部,直到他们提供一个合适的数学库.

  • 请注意,这一切只是为了展示您原始建议的智慧:**在他们提供正确的数学库之前,大致打击您的编译器供应商,直到他们提供正确的数学库.**这些正是我们图书馆实施者所支付的那类问题担心,我们图书馆的用户*不应该*必须处理它们. (2认同)