c++ - 符合 IEEE-754 标准的四舍五入

标签 c++ c floating-point ieee-754 bankers-rounding

C 标准库提供了 round , lround , 和 llround C99 中的函数族。但是,这些函数不符合 IEEE-754,因为它们没有实现 IEEE 规定的半对偶的“银行家四舍五入”。如果小数部分恰好为 0.5,则半对偶四舍五入要求将结果四舍五入到最接近的偶数。如 cppreference.com 中所述,C99 标准改为要求离零一半。

1-3) Computes the nearest integer value to arg (in floating-point format), rounding halfway cases away from zero, regardless of the current rounding mode.

在 C 中实现舍入的常用方法是表达式 (int)(x + 0.5f)其中,尽管是incorrect在严格的 IEEE-754 数学中,通常由编译器翻译成正确的 cvtss2si操作说明。然而,这当然不是一个可移植的假设。

我如何实现一个函数,该函数将使用半对偶语义对任何浮点值进行舍入?如果可能,该函数应仅依赖于语言和标准库语义,以便它可以在非 IEEE 浮点类型上运行。如果这不可能,根据 IEEE-754 位表示定义的答案也是可以接受的。请根据 <limits.h> 描述任何常数或 <limits> .

最佳答案

The C standard library provides the round, lround, and llround family of functions in C99. However, these functions are not IEEE-754 compliant, because they do not implement the "banker's rounding" of half-to-even as mandated by IEEE...

谈论某个功能是否“符合 IEEE-754”是没有意义的。 IEEE-754 合规性要求一组具有已定义语义的数据类型操作可用。它不要求那些类型或操作具有特定名称,也不要求那些操作可用。一个实现可以提供它想要的任何附加功能并且仍然是兼容的。如果一个实现想要提供舍入到奇数、舍入随机、舍入零和不精确的陷阱,它可以这样做。

IEEE-754 对舍入的实际要求是提供以下六种操作:

convertToIntegerTiesToEven(x)

convertToIntegerTowardZero(x)

convertToIntegerTowardPositive(x)

convertToIntegerTowardNegative(x)

convertToIntegerTiesToAway(x)

convertToIntegerExact(x)

在 C 和 C++ 中,这些操作的最后五个绑定(bind)到 truncceilfloorroundrint 函数。 C11 和 C++14 第一个没有绑定(bind),但 future 的修订将使用 roundeven。如您所见,round 实际上是必需的操作之一。

但是,roundeven 在当前的实现中不可用,这将我们带到您问题的下一部分:

The usual ad-hoc way to implement rounding in C is the expression (int)(x + 0.5f) which, despite being incorrect in strict IEEE-754 math, is usually translated by compilers into the correct cvtss2si instruction. However, this is certainly not a portable assumption.

该表达式的问题远远超出了“严格的 IEEE-754 数学”。它对负 x 完全不正确,对 nextDown(0.5) 给出错误答案,并将 2**23 binade 中的所有奇数整数转换为偶数整数。任何将它翻译成 cvtss2si 的编译器都非常非常糟糕。如果您有发生这种情况的示例,我很乐意看到。

How can I implement a function that will round any floating point value with half-to-even semantics?

正如 njuffa 在评论中指出的那样,您可以确保设置默认舍入模式并使用 rint(或 lrint,因为它听起来您实际上想要一个整数结果),或者您可以通过调用 round 然后像 gnasher729 建议的那样修复中间情况来实现您自己的舍入函数。一旦采用了 C 的 n1778 绑定(bind),您就可以使用 roundevenfromfp 函数执行此操作,而无需控制舍入模式。

关于c++ - 符合 IEEE-754 标准的四舍五入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32746523/

相关文章:

C++ `digits10` 对于 IEEE float 是 6,但第一个不可表示的整数已经有 8 位数字?

C++ static_cast<void *>

c++ - 使用 QRegExp 的 Ingore 字符串

c - 使用 fgetc() 逐行读取 c 文件

c - C 语言中的补码和信息丢失

c++ - 在 C++ 中除以 2 个 float 会根据使用的方法产生不同的结果

JavaScript 浮点好奇心

c++ - Arduino上Serial和Stream有什么区别,Serial.write是如何实现的?

c++ - 使用位字段以 4 位增量访问给定类型的各个位组

c - 应该使用哪些配置位来调试 PIC16F1947?