C 标准库提供 C99 中的round
、lround
和llround
系列函数。但是,这些函数不符合 IEEE-754 标准,因为它们没有按照 IEEE 的要求实现半对偶的“银行家四舍五入”……
谈论单个功能是否“符合 IEEE-754”是没有意义的。IEEE-754 合规性要求一组具有定义语义的数据类型操作可用。它不要求这些类型或操作具有特定名称,也不要求只有这些操作可用。一个实现可以提供它想要的任何附加功能并且仍然是合规的。如果一个实现想要提供奇数舍入、随机舍入、零舍入和不精确时陷阱,它可以这样做。
IEEE-754 对舍入的实际要求是提供了以下六种操作:
convertToIntegerTiesToEven (x)
convertToIntegerTowardZero (x)
convertToIntegerTowardPositive (x)
convertToIntegerTowardNegative (x)
convertToIntegerTiesToAway (x)
convertToIntegerExact (x)
在 C 和 C++ 中,最后五个操作分别绑定到trunc
、ceil
、floor
、round
和rint
函数。C11 和 C++14 第一个没有绑定,但未来的修订版将使用roundeven
. 如您所见,round
实际上是必需的操作之一。
但是,roundeven
在当前实现中不可用,这将我们带到您问题的下一部分:
在 C 中实现舍入的常用特别方法是表达式(int)(x + 0.5f)
,尽管在严格的 IEEE-754 数学中不正确,但通常由编译器翻译成正确的cvtss2si
指令。然而,这当然不是一个可移植的假设。
该表达式的问题远远超出了“严格的 IEEE-754 数学”。负数是完全不正确的x
,给出了错误的答案nextDown(0.5)
,并将 2**23 binade 中的所有奇数转换为偶数。任何将其翻译成的编译器cvtss2si
都非常糟糕。如果您有发生这种情况的示例,我很乐意看到它。
如何实现一个函数,该函数将使用半偶数语义舍入任何浮点值?
正如njuffa在评论中指出的那样,您可以确保设置和使用默认舍入模式rint
(或者lrint
,听起来您实际上想要一个整数结果),或者您可以通过调用round
然后修复中途情况来实现自己的舍入函数就像gnasher729建议的那样。一旦采用了 C 的 n1778 绑定,您就可以使用roundeven
orfromfp
函数来执行此操作,而无需控制舍入模式。