当前位置:  开发笔记 > 编程语言 > 正文

round()for C++中的float

如何解决《round()forC++中的float》经验,为你挑选了10个好方法。

我需要一个简单的浮点舍入函数,因此:

double round(double);

round(0.1) = 0
round(-0.1) = 0
round(-0.9) = -1

我能找到ceil()floor()在math.h中-但不是round().

它是以另一个名称存在于标准C++库中,还是缺少?



1> Andreas Magn..:

C++ 98标准库中没有round().你可以自己写一个.以下是圆形上半部分的实现:

double round(double d)
{
  return floor(d + 0.5);
}

C++ 98标准库中没有循环函数的可能原因是它实际上可以以不同的方式实现.以上是一种常见的方式,但还有其他一些方法,例如圆形到偶数,如果你要进行大量的四舍五入,那么偏差较小,通常会更好; 但实施起来有点复杂.


这不能正确处理负数.litb的回答是正确的.
在截断之前添加0.5无法舍入到几个输入的最接近的整数,包括0.49999999999999994.见http://blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1
@InnerJoin:是的,它处理负数与litb的答案不同,但这并不会使它"不正确".
@MuhammadAnnaqeeb:你是对的,自从C++ 11发布以来,事情已经有了很大的改进.这个问题在另一个生活困难,欢乐很少的时候得到了回答.它仍然留在这里作为对当时生活和反抗的英雄以及那些仍然无法使用现代工具的可怜灵魂的颂歌.
@ Sergi0:没有"正确"和"不正确",因为有[不止一个舍入的定义](http://en.wikipedia.org/wiki/Rounding#Tie-breaking)决定在中途发生的事情点.通过判断前,请检查您的事实.
@SSpoke:它向上舍入到最接近的较大整数.-2> -3.它被称为"Round Half Up":http://en.wikipedia.org/wiki/Rounding#Round_half_up
@Roddy不,它将负数舍入不正确的事实使它不正确.
@Roddy考虑到Pascal Couq的三篇文章,在上面的评论中,关于这个操作有多么棘手,这个答案还不够.任何声称自己动手的答案都必须非常长.20多个downvotes也应该强烈表明这个答案有问题.
无需定义它.它符合C++ 11标准

2> Daniel Wolf..:

Boost提供了一组简单的舍入功能.

#include 

double a = boost::math::round(1.5); // Yields 2.0
int b = boost::math::iround(1.5); // Yields 2 as an integer

有关更多信息,请参阅Boost文档.

编辑:既然C++ 11,还有std::round,std::lroundstd::llround.


我已经在我的项目中使用了boost,为此+1,比使用天真的`floor(value + 0.5)`方法要好得多!

3> Shafik Yaghm..:

C++ 03标准依赖于C90标准,标准称为标准C库,C++ 03标准草案(最接近 C++ 03的公开标准草案是N1804)部分1.2 规范性引用:

ISO/IEC 9899:1990的第7节中描述的库和ISO/IEC 9899/Amd.1:1995的第7节在下文中称为标准C库.1)

如果我们在cppreference上找到round,lround,llround的C文档,我们可以看到round和相关函数是C99的一部分,因此在C++ 03或之前不可用.

在C++ 11中,这种情况发生了变化,因为C++ 11依赖于C标准库的C99草案标准,因此提供了std :: round和整数返回类型std :: lround,std :: llround:

#include 
#include 

int main()
{
    std::cout << std::round( 0.4 ) << " " << std::lround( 0.4 ) << " " << std::llround( 0.4 ) << std::endl ;
    std::cout << std::round( 0.5 ) << " " << std::lround( 0.5 ) << " " << std::llround( 0.5 ) << std::endl ;
    std::cout << std::round( 0.6 ) << " " << std::lround( 0.6 ) << " " << std::llround( 0.6 ) << std::endl ;
}

来自C99的另一个选项是std :: trunc,其中:

计算最大整数,其大小不大于arg.

#include 
#include 

int main()
{
    std::cout << std::trunc( 0.4 ) << std::endl ;
    std::cout << std::trunc( 0.9 ) << std::endl ;
    std::cout << std::trunc( 1.1 ) << std::endl ;

}

如果你需要支持非C++ 11应用程序,你最好的选择是使用boost round,iround,lround,llround或boost trunc.

滚动你自己的版本很难

滚动你自己可能不值得努力,比起看起来更难:将浮动舍入到最接近的整数,第1部分,舍入浮点数到最接近的整数,第2部分和舍入浮点数到最接近的整数,第3部分解释:

例如,使用std::floor和添加实现的常见滚动0.5不适用于所有输入:

double myround(double d)
{
  return std::floor(d + 0.5);
}

这将失败的一个输入是0.49999999999999994,(见它直播).

另一个常见的实现涉及将浮点类型转换为整数类型,在整数部分无法在目标类型中表示的情况下,可以调用未定义的行为.我们可以从C++标准部分的4.9 浮动积分转换中看到这一点,它说(强调我的):

可以将浮点类型的prvalue转换为整数类型的prvalue.转换截断; 也就是说,丢弃小数部分.如果截断的值无法在目标类型中表示,则行为未定义.[...]

例如:

float myround(float f)
{
  return static_cast( static_cast( f ) ) ;
}

由于std::numeric_limits::max()4294967295那么下面的电话:

myround( 4294967296.5f ) 

会导致溢出,(见它直播).

通过查看在C中实现round()的简洁方法的答案,我们可以看出这是多么困难?引用newlibs版本的单精度浮动圆.这对于看似简单的事情来说是一个非常长的功能.没有对浮点实现有深入了解的人似乎不太可能正确地实现这个功能:

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

另一方面,如果其他解决方案都不可用,则newlib可能是一个选项,因为它是一个经过良好测试的实现.


@downvoter请解释一下可以改进哪些方面?这里的绝大多数答案都是错误的,因为他们试图推出自己的回合,这种回合都以某种形式失败.如果我的解释中缺少某些内容,请告诉我.
@chux有趣,IEEE 754-2008标准确实指出舍入保留了零和无穷大的符号(见5.9).

4> kalaxy..:

值得注意的是,如果你想要一个舍入的整数结果,你不需要通过ceil或floor.也就是说,

int round_int( double r ) {
    return (r > 0.0) ? (r + 0.5) : (r - 0.5); 
}


不会给出0.49999999999999994的预期结果(当然,取决于你的期望,但0对我来说似乎比1更合理)
根据4.9 [conv.fpint],*“如果无法在目标类型中表示截断的值,则该行为未定义。” *,这有点危险。其他SO答案描述了如何稳健地执行此操作。

5> 小智..:

自C++ 11以来就可以使用它(根据http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf)

#include 
#include 

int main(int argc, char** argv) {
  std::cout << "round(0.5):\t" << round(0.5) << std::endl;
  std::cout << "round(-0.5):\t" << round(-0.5) << std::endl;
  std::cout << "round(1.4):\t" << round(1.4) << std::endl;
  std::cout << "round(-1.4):\t" << round(-1.4) << std::endl;
  std::cout << "round(1.6):\t" << round(1.6) << std::endl;
  std::cout << "round(-1.6):\t" << round(-1.6) << std::endl;
  return 0;
}

输出:

round(0.5):  1
round(-0.5): -1
round(1.4):  1
round(-1.4): -1
round(1.6):  2
round(-1.6): -2



6> MSN..:

它通常被实现为floor(value + 0.5).

编辑:它可能不会被调用,因为我知道至少有三种舍入算法:舍入到零,舍入到最接近的整数,以及银行家的舍入.你要求舍入到最接近的整数.


确实存在不同的舍入算法,它们都可以合理地声称是"正确的".但是,楼层(值+ 0.5)不是其中之一.对于某些值,例如0.49999997f或等效的double,答案是错误的 - 当所有人都同意它应该为零时,它将四舍五入为1.0.有关详细信息,请参阅此帖子:http://blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1

7> 小智..:

我们正在考虑两个问题:

    四舍五入转换

    类型转换.

舍入转换意味着舍入±float/double到最接近的floor/ceil float/double.可能是你的问题在这里结束.但是如果您希望返回Int/Long,则需要执行类型转换,因此"溢出"问题可能会影响您的解决方案.所以,检查你的功能是否有错误

long round(double x) {
   assert(x >= LONG_MIN-0.5);
   assert(x <= LONG_MAX+0.5);
   if (x >= 0)
      return (long) (x+0.5);
   return (long) (x-0.5);
}

#define round(x) ((x) < LONG_MIN-0.5 || (x) > LONG_MAX+0.5 ?\
      error() : ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))

来自:http://www.cs.tut.fi/~jkorpela/round.html



8> Philipp..:

在Boost中也实现了某种类型的舍入:

#include 

#include 

template T round2(const S& x) {
  typedef boost::numeric::conversion_traits Traits;
  typedef boost::numeric::def_overflow_handler OverflowHandler;
  typedef boost::numeric::RoundEven Rounder;
  typedef boost::numeric::converter Converter;
  return Converter::convert(x);
}

int main() {
  std::cout << round2(0.1) << ' ' << round2(-0.1) << ' ' << round2(-0.9) << std::endl;
}

请注意,这仅适用于进行整数转换.


Boost还提供一组简单的舍入功能; 看到我的回答.

9> Carl..:

您可以使用以下方法舍入到n位精度:

double round( double x )
{
const double sd = 1000; //for accuracy to 3 decimal places
return int(x*sd + (x<0? -0.5 : 0.5))/sd;
}


除非你的编译器int size默认为1024位,否则这对于巨大的双倍来说是不准确的...
当然,但问题是通用轮,你无法控制如何使用它.在没有ceil和floor的情况下,没有理由进行回合失败.

10> dshin..:

如果您最终想要将函数的double输出转换round()为a int,那么此问题的可接受解决方案将类似于:

int roundint(double r) {
  return (int)((r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5));
}

当以均匀随机值传递时,我的机器上的时钟大约为8.88 ns.

据我所知,以下在功能上是等效的,但在我的机器上的时钟频率为2.48 ns,具有显着的性能优势:

int roundint (double r) {
  int tmp = static_cast (r);
  tmp += (r-tmp>=.5) - (r-tmp<=-.5);
  return tmp;
}

性能更好的原因之一是跳过分支.

推荐阅读
ar_wen2402851455
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有