为什么很多(旧)程序都使用floor(0.5 +输入)而不是round(输入)?


80

差异在于返回值,我相信这种情况会给平局提供输入,例如以下代码

int main()
{
    std::cout.precision(100);

    double input = std::nextafter(0.05, 0.0) / 0.1;
    double x1 = floor(0.5 + input);
    double x2 = round(input);

    std::cout << x1 << std::endl;
    std::cout << x2 << std::endl;
}

输出:

1
0

但是最终它们只是不同的结果,一个选择了自己喜欢的一个。我看到许多使用floor(0.5 + input)而不是的“旧” C / C ++程序round(input)

有什么历史原因吗?最便宜的CPU?


20
std :: round将个案从零移到一半。这在数学上并不统一,例如floor(f + .5),中途情况总是朝着上侧。使用地板法的原因是在工程领域进行正确的四舍五入是必需的。
麦克罗伊

7
正如在C ++之前的C ++ 11中对float的round()中指出的那样,我们没有回合。正如我在回答中指出的那样,正确编写自己的回合是一个难题。
Shafik Yaghmour

16
@Arne使用std :: round()的-0.5和+0.5的四舍五入值之间的距离为2。使用floor,则为1。仅当两个值具有相反的符号时才会发生。尝试绘制直线时非常烦人,或者使您选择错误的纹理像素。
麦克罗伊

20
某些编程语言和环境(包括.NET)使用一种称为Banker's Rounding的欺骗性东西,其中x.5会四舍五入到最接近的偶数。因此,将0.5舍入为0,而将1.5舍入为2。您可以想象这在调试时会引起混乱。我认为解决此邪恶“功能”的方法是根本不具有.Round()函数,而具有.RoundBankers()、. RoundHalfUp()、. RoundHalfDown()等(或.BankersRound()等)但是intellisense与.RoundBankers()一起使用会更好。至少那样一来,您将被迫知道会发生什么。
user3685427 '17

8
@ user3685427:在需要消除舍入带来的细微和系统性的向上偏差的金融和统计应用中,必须进行银行家舍入。如果没有实际的硬件浮点实现知识,几乎是不可能实现的,因此在C#中它是默认选择。
Pieter Geerkens

Answers:


115

std::round是在C ++ 11中引入的。在此之前,只有std::floor可用,以便程序员使用它。


没有。但是它比C ++ 11老。我认为C ++早在11之前就已经合逻辑了。仅此而已;)
markzzz

12
@markzzz-C ++标准库不会自动继承C的标准库。请仔细选择并继续进行。他们花了12年时间才能同步到C99。
StoryTeller-Unslander Monica's

2
@haccks:的确如此。IMHO C在数学函数方面一直领先于C ++,直到C ++ 11。
Bathsheba

3
重要的是要注意,所使用的方法对于某些输入可能会中断,并且C ++ 11依赖于C99,而C ++ 03则依赖于C90,这会到达@markzzz点。
Shafik Yaghmour

1
@markzzz:尽管有批评者,但您的发言是当场的。事实是,没有C ++标准还有很长的差距,第一个C ++标准是C ++ 98,而第一个主要版本是C ++ 11。有一个小的更新,C ++ 03,但是正如Wikipedia页面所指出的那样,它主要是一个“错误修复”版本。因此,经过13年的努力,C ++ 11是第一个赶上C标准库的机会。
Matthieu M.

21

没有任何历史原因。自从一年以来,这种偏差就已经存在。人们在感到非常顽皮时会这样做。这是对浮点算法的滥用,许多经验丰富的专业程序员都喜欢它。甚至Java Bod都可以升级到1.7版。有趣的家伙。

我的猜测是,在C ++ 11之前(尽管C在C99中获得了它),才正式提供了一个开箱即用的体面的德国舍入函数,但这确实不是采用所谓替代方法的借口。

事情是这样的: floor(0.5 + input)并不总是能恢复与相应std::round呼叫相同的结果!

原因是非常微妙的:根据宇宙的巧合性质,a.5对于整数而言a,德国舍入的截止点是二进位有理数。因为这可以精确地表示为IEEE754浮点数,直到52的2的幂,然后舍入仍然是无操作,std::round始终可以正常工作。有关其他浮点方案,请查阅文档。

但是添加0.5到adouble会引入不精确性,从而导致某些值出现轻微的过冲或过冲。如果您考虑一下,将两个double值加在一起(这是不经意间拒绝转换的开始),然后应用一个非常强大的输入函数(例如舍入函数)的函数,注定要以失败告终。

不要这样做

参考:为什么Math.round(0.49999999999999994)返回1?


2
评论不作进一步讨论;此对话已转移至聊天
安迪

2
nearbyint()通常是比更好的选择round(),因为nearbyint使用当前的舍入模式而不是从零开始的时髦抢七游戏round()(尽管x86甚至没有硬件支持,但ARM确实支持)。两者均已在C ++ 11中添加到C ++。
彼得·科德斯

9

我认为这是您犯错的地方:

但是最终它们只是不同的结果,一个选择了自己喜欢的一个。我看到许多使用floor(0.5 +输入)而不是round(输入)的“旧” C / C ++程序。

事实并非如此。您必须为域选择正确的舍入方案。在金融应用程序中,您将使用银行家规则取整(顺便说一下,不要使用浮点数)。但是,在采样时,使用进行四舍五入会static_cast<int>(floor(f + .5))产生较少的采样噪声,这会增加动态范围。当对齐像素时,即将位置转换为屏幕坐标时,使用任何其他舍入方法将产生孔,间隙和其他伪像。


“这增加了动态范围”-看起来像无意义的多余文本;从某个地方复制粘贴错误?可能要删除它。
anatolyg

不会。降低采样噪声会降低本底噪声,确实会增加动态范围。
麦克罗伊

您能否提供一个简单的示例或参考来说明动态范围的增加/噪声的减少?
罗斯兰

1
使用标准整数(无舍入)舍入时,介于零和负一之间的所有值都会“消失”,或者更改符号(相同的值以及从0到+ 1的输入),所有负值至少偏移一位。这是一处杂乱无章的噪音。
米歇尔·罗伊

4

一个简单的原因可能是有不同的四舍五入方法,因此,除非您知道所使用的方法,否则结果可能会不同。

使用floor(),您可以使结果一致。如果float为.5或更大,则将其添加到下一个int。但是.49999只会删除小数点。


+1这个问题的答案是由对问题的评论所提出的,其中对所做的事情存在分歧round()
Loduwijk

@Aaron答案是错误的floor(x + 0.5),尽管这样做。
EOF

哦,哈哈!那好。具有讽刺意味的。“使用众所周知的X,因为我们对Y并不认同或不完全了解。” 那么,当X应用于X时,您会怎么做?
Loduwijk

1
@亚伦·易 您只需做一次理智的操作nearbyint(x),然后使用,只要您不弄乱浮点环境,就可以使用理智(至最接近的偶数)取整。
EOF

@EOF:您的舍入选择不是理智的。在非线性分量中不具​​有周期1的舍入函数是不可思议的。
Eric Towers

2

许多程序员会采用他们在使用其他语言进行编程时学到的习惯用法。并非所有语言都具有round()功能,在这些语言中,通常可以使用它来floor(x + 0.5)代替。当这些程序员开始使用C ++时,他们并不总是意识到内置的功能,而是round()继续使用他们惯用的样式。

换句话说,仅仅因为您看到许多代码可以执行某项操作,并不意味着有充分的理由这样做。您可以在每种编程语言中找到此示例。记住St鱼定律

百分之九十的东西都是垃圾

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.