一对几乎为 1 的值相乘可以得到 1.0 的结果吗?

Cor*_*ica 2 c++ floating-point

我有两个浮点值,ab。我可以保证它们是域 (0, 1) 中的值。有什么情况a * b可以等于一吗?我打算计算1/(1 - a * b),并希望避免被零除。

我的直觉是它不能,因为结果应该等于或小于ab。但本能并不能替代理解正确的行为。

我无法指定舍入模式,因此如果存在可能会遇到麻烦的舍入模式,我想了解它。

编辑:我没有指定编译器是否符合 IEEE 标准,因为我无法保证运行我的软件的编译器/CPU 确实符合 IEEE 标准。

Eri*_*hil 6

\n

我有两个浮点值,ab\xe2\x80\xa6

\n
\n

由于这表示我们有 \xe2\x80\x9cvalues,\xe2\x80\x9d 而不是 \xe2\x80\x9cvariables,\xe2\x80\x9d 它承认可能评估为 1 的可能性1 - a*b。在编写软件时,人们有时会使用名称作为更复杂表达式的占位符。例如,一个人可能有一个表达式asin(x)/x一个表达式b,然后在代码实际上是 时1-y*y询问计算问题。这将是一个问题,因为 C++ 允许在计算浮点表达式时使用额外的精度。1 - a*b1 - (sin(x)/x)*(1-y*y)

\n

最常见的实例是编译器long double在计算包含操作数的表达式时使用算术double,或者在计算格式的表达式时使用融合乘加指令x + y*z

\n

假设表达式ab是以超额精度计算的,并且在该超额精度中是小于 1 的正值。例如,为了说明起见,假设double是用四位十进制数字实现的,但ab是用六位十进制数字来计算的long doublea并且b都可以是 .999999。然后a*b是四舍五入前的 0.999998000001,四舍五入后的 0.999998。现在假设在计算的这一点上,编译器从 转换为long doubledouble可能是因为它决定在从附近的表达式计算其他一些内容时暂时将该中间值存储在堆栈上。将其转换为四位数字double会产生 1.000,因为这是最接近 0.999998 的四位十进制数字。当编译器稍后从堆栈加载它并继续计算时,我们得到1 - 1.000,并且结果为零。

\n

另一方面,如果ab是变量,我希望你的表达式是安全的。当将值分配给变量或使用强制类型转换操作进行转换时,C++ 标准要求将其转换为名义类型;结果必须是名义类型的值,没有任何 \xe2\x80\x9c 额外精度。\xe2\x80\x9d 然后,给定 0 < a< 1 和 0 < b< 1,数学值(即,没有浮点四舍五入)a\xe2\x80\xa2b小于a且小于b。然后将a\xe2\x80\xa2舍入b到标称类型不能产生大于ab使用任何 IEEE-754 舍入方法的值,因此它不能产生 1。(这里唯一的要求是舍入方法永远不会跳过值\xe2 \x80\x94 它可能被限制在特定方向上舍入,向上或向下或朝向零或其他方向,但它永远不会超过该方向上的可表示值以达到远离未舍入结果的值。因为我们知道a\ xe2\x80\xa2以和b为界,舍入不能产生大于和中较小者的任何结果。)abab

\n

从形式上来说,C++ 标准对浮点结果的精度没有任何要求。因此,C++ 实现可以使用疯狂的舍入模式,为 0.9*.9 生成 3.14。除了将次正规数刷新为零的实现之外,我不知道有任何 C++ 实现不遵守上述要求。1 - a*ba和接近 1 时,将次正规值刷新为零不会影响计算。b(在反常的浮点格式中,指数范围比有效数窄且没有次正规值,0.9999 可以表示,而 0.0001 则不能表示,因为指数它超出了范围。然后1-.9999*.9999,在正常的四位算术中会产生 0.0002,由于下溢,会产生 0。普通硬件中没有这样的格式。)

\n

因此,如果ab是变量,0 < a< 1 和 0 < b< 1,并且您的 C++ 实现是合理的(可能使用额外的精度,可能刷新次正规数,不使用反常的浮点格式或舍入),则1 - a*b不会计算为零。

\n