在 C 中实现 round() 的简洁方法? [英] Concise way to implement round() in C?

查看:31
本文介绍了在 C 中实现 round() 的简洁方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

谢谢,弗雷德

解决方案

正如许多其他答案所暗示的那样,您可以重新发明轮子.或者,您可以使用其他人的轮子——我建议使用 Newlib 的轮子,它是 BSD 许可的,旨在用于嵌入式系统.它可以正确处理负数、NaN、无穷大和不能表示为整数的情况(由于太大),并且以使用指数和掩码而不是通常更昂贵的浮点运算的有效方式进行处理.此外,它还定期进行测试,因此您知道它没有明显的角落问题.

Newlib 源代码浏览起来可能有点笨拙,所以这里是您想要的部分:

浮动版:https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=newlib/libm/common/sf_round.c;hb=masterp>

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

此处定义的单词提取宏:https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=newlib/libm/common/fdlibm.h;hb=masterp>

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

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

float roundf(x){整数符号位;__uint32_t w;/* 最重要的词,最不重要的词.*/int exponent_less_127;GET_FLOAT_WORD(w, x);/* 提取符号位.*/符号位 = w &0x80000000;/* 提取指数字段.*/exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;如果 (exponent_less_127 < 23){如果 (exponent_less_127 < 0){w &= 0x80000000;如果(exponent_less_127 == -1)/* 结果是 +1.0 或 -1.0.*/w |= ((__uint32_t)127 <<23);}别的{无符号整数指数掩码 = 0x007fffff >>exponent_less_127;if ((w & exponent_mask) == 0)/* x 有一个整数值.*/返回 x;w += 0x00400000>>exponent_less_127;w &= ~exponent_mask;}}别的{如果(exponent_less_127 == 128)/* x 是 NaN 或无穷大.*/返回 x + x;别的返回 x;}SET_FLOAT_WORD(x, w);返回 x;}

The embedded C I'm using doesn't have a round() function it it's math lib, what would be a concise way to implement this in C? I was thinking of printing it to a string, looking for the decimal place, then finding the first char after the period, then rounding up if >= 5, else down. etc. Was wondering if there's something more clever.

Thanks, Fred

解决方案

You could re-invent the wheel, as many other answers suggest. Alternately, you could use someone else's wheel -- I'd suggest Newlib's, which is BSD-licensed and intended for use on embedded systems. It properly handles negative numbers, NaNs, infinities, and cases which are not representable as integers (due to being too large), as well as doing so in an efficient manner that uses exponents and masking rather than generally-costlier floating-point operations. In addition, it's regularly tested, so you know it doesn't have glaring corner-case bugs in it.

The Newlib source can be a bit awkward to navigate, so here are the bits you want:

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

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

Word-extraction macros defined here: https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=newlib/libm/common/fdlibm.h;hb=master

If you need other files from there, the parent directory is this one: https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=tree;f=newlib/libm/common;hb=master

For the record, here's the code for the float version. As you can see, there's a bit of complexity required to deal with all the possible cases correctly.

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;
}

这篇关于在 C 中实现 round() 的简洁方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆