我需要一个简单的浮点舍入函数,因此:
double round(double); round(0.1) = 0 round(-0.1) = 0 round(-0.9) = -1
我能找到ceil()
和floor()
在math.h中-但不是round()
.
它是以另一个名称存在于标准C++库中,还是缺少?
C++ 98标准库中没有round().你可以自己写一个.以下是圆形上半部分的实现:
double round(double d) { return floor(d + 0.5); }
C++ 98标准库中没有循环函数的可能原因是它实际上可以以不同的方式实现.以上是一种常见的方式,但还有其他一些方法,例如圆形到偶数,如果你要进行大量的四舍五入,那么偏差较小,通常会更好; 但实施起来有点复杂.
Boost提供了一组简单的舍入功能.
#includedouble 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::lround
和std::llround
.
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
是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可能是一个选项,因为它是一个经过良好测试的实现.
值得注意的是,如果你想要一个舍入的整数结果,你不需要通过ceil或floor.也就是说,
int round_int( double r ) { return (r > 0.0) ? (r + 0.5) : (r - 0.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
它通常被实现为floor(value + 0.5)
.
编辑:它可能不会被调用,因为我知道至少有三种舍入算法:舍入到零,舍入到最接近的整数,以及银行家的舍入.你要求舍入到最接近的整数.
我们正在考虑两个问题:
四舍五入转换
类型转换.
舍入转换意味着舍入±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
在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; }
请注意,这仅适用于进行整数转换.
您可以使用以下方法舍入到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; }
如果您最终想要将函数的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; }
性能更好的原因之一是跳过分支.