C标准库在C99中提供 round
, lround
和 llround
系列函数 . 但是,这些功能不符合IEEE-754标准,因为它们没有实现IEEE规定的半到均匀的"banker's rounding" . 如果小数分量恰好为0.5,则半到均匀舍入要求将结果四舍五入到最接近的偶数值 . 相反,C99标准规定半径为零,如cppreference.com所述
1-3)计算最接近arg的整数值(浮点格式),将中间情况舍入为零,不管当前的舍入模式如何 .
在C中实现舍入的通常的临时方法是表达式 (int)(x + 0.5f)
,尽管在严格的IEEE-754数学中是incorrect,但通常由编译器将其转换为正确的 cvtss2si
指令 . 然而,这肯定不是一个可移植的假设 .
如何实现一个函数,它将使用半对偶语义对任何浮点值进行舍入?如果可能,该函数应仅依赖于语言和标准库语义,以便它可以在非IEEE浮点类型上运行 . 如果这不可能,则根据IEEE-754位表示定义的答案也是可接受的 . 请根据 <limits.h>
或 <limits>
来表征任何常量 .
5 回答
将数字x舍入,如果x和round(x)之间的差值恰好为0.5或-0.5,并且round(x)为奇数,则round(x)在错误的方向上舍入,因此您减去x的差值 .
谈论个别功能是否是"IEEE-754 compliant"是没有意义的 . IEEE-754合规性要求具有定义语义的一组数据类型操作可用 . 它不要求这些类型或操作具有特定名称,也不要求只有那些操作可用 . 实现可以提供它想要的任何附加功能并且仍然是兼容的 . 如果一个实现想要提供舍入到奇数,舍入随机,舍入为零和陷阱 - 如果不精确,它可以这样做 .
IEEE-754实际需要进行四舍五入的是提供以下六种操作:
在C和C中,这些操作的最后五个分别绑定到
trunc
,ceil
,floor
,round
和rint
函数 . C11和C14没有第一个绑定,但未来的修订将使用roundeven
. 如您所见,round
实际上是必需的操作之一 .但是,
roundeven
在当前的实现中不可用,这将我们带到您问题的下一部分:该表达式的问题远远超出"strict IEEE-754 math" . 负
x
完全不正确,为nextDown(0.5)
给出了错误的答案,并将2 ** 23 binade中的所有奇数整数变为偶数整数 . 任何将它翻译成cvtss2si
的编译器都是可怕的,可怕的破碎 . 如果你有一个这样的例子,我很乐意看到它 .正如评论中提到的那样,您可以确保设置默认舍入模式并使用
rint
(或lrint
,因为它听起来您实际上想要一个整数结果),或者您可以通过调用round
然后修复来实现自己的舍入功能像 gnasher729 建议的中途病例 . 一旦采用了针对C的n1778绑定,您将能够使用roundeven
或fromfp
函数执行此操作,而无需控制舍入模式 .使用C标准库中的
remainder(double x, 1.0)
. 这与当前的舍入模式无关 .remainder()
在这里很有用,因为它符合OP与甚至要求的关系 .测试代码
产量
float
数据类型可以表示所有整数,但不包含分数,范围在8388608.0f到16777216.0f之间 . 任何大于8388607.5f的float
数字都是整数,不需要舍入 . 将8388608.0f添加到任何小于该值的非负float
将产生一个整数,该数将根据当前的舍入模式(通常为舍入半舍入到偶数)进行舍入 . 减去8388608.0f然后将产生原始的正确圆形版本(假设它在合适的范围内) .因此,应该可以做类似的事情:
并利用添加的自然舍入行为,而不必使用任何其他“舍入到整数”设施 .
以下是 round-half to even 程序的简单实现,该程序遵循IEEE的舍入标准 .
Python 2.7 implementation:
C++ implementation: (使用math.h作为楼层功能)
这将给出的输出如下 . 标准:
Edit 1 : 正如@Mark Dickinson在评论中指出的那样 . 可以根据代码中的要求修改错误以使其标准化 . 对于python,要将其转换为可能的最小浮点值,您可以执行以下操作 .