两个浮点数的除法为-1.0是否意味着它们相等但符号相反



由于精度的原因,在大多数编程语言中,0.1 + 0.2不等于0.3。然而,

var float a, float b
assert(a != 0 && b != 0)
if a / b == -1.0 {
// a, b are opposite numbers:
//  abs(a) == abs(b) && (a > 0 && b < 0 || a < 0 && b > 0)
...
}

如果非零ab是相反的数字(它们的绝对值相同,但一个是正的,另一个是负的),则总是真的吗

或者我必须引入一个非常小的数字epsilon(例如1e-7)来消除错误:

if a / b >= -1.0 - epsilon && a / b <= -1.0 + epsilon

首先,a/b=−1不是真的⇔a=−b。正如问题中的示例代码所建议的那样,这对于a=0、b=0不成立,因为a/b产生NaN。另一种情况是a=±∞,b=∓∞,其中a/b也产生NaN。

另一种情况发生在使用大于2的基数的格式中。考虑两位数的十进制基本格式。a=−1.0•102b=+9.9•101时,a/b为−1.0101010…,四舍五入(四舍五舍五入)到两位得出−1.0,因此a/b为−1。

在向上或向零取整的基数为2的格式中,取a=−1和b=下一个大于−1的可表示值,将为a/b产生−1,如下所述。

排除这些情况(NaN商,基数大于2,向上取整为−1),a/b=−1是正确的⇔a=−b。一个证明随之而来。

考虑任何浮点格式,其中有限数表示为±d0d−1d−2d<1->pbe,其中b为固定基数,e=设定限内的指数,p是精度(位数),数字d:/em>是0≤di<b

对于不同大小的ab,如果ab的大小是格式中的相邻数字,则最接近的a/b(注意,a/b是浮点运算中计算a/b的结果,而a/ba除以b的实数商,没有浮点舍入)可以为−1,特别是如果,对于adi=b−1,则a=±(be+1be+1-p),并且b=∓。则a/b=−1+b-p)。在a/b=−1+b-p中,确切的结果−1+b-p是一个远离−1的完整ULP(e=−1指数区间),因此a/b的浮点结果将是−1+b>-p

然而,我们还必须考虑这样一种情况,其中a是±beb是下一个较低的值,但被否定了∓(b>ebe-p[/sup>)。则a/b为−be/(bebe-pp/sup>)=−1/(1−bp)。这不是商在不为−1的情况下最接近−1的值,但它使商的大小略大于1,使其处于ULP较大的指数区间。即使e=−1区间的ULP与−1的距离大于1,e=0区间的ULP也要大b倍。商略高于−1的1/bULP。如果b为2,则商从−1起大于½ULP,因此,通过四舍五入到最近值,它将四舍五进到下一个可表示的值。如果b是任何较大的整数,则商从−1小于½ULP,因此它将四舍五入到−1。因此,a/b=−1⇔a=−b在非基本的两种格式中都不成立,即使不包括NaN商的情况。它也将不适用于具有一些非默认舍入模式的基本两种格式,例如向零舍入。

对于基数为2的格式,并向最近值取整,a/b=−1表示a=-b。只要CCD_ 56不是NaN,则反之亦然。

  1. 正如IEEE 754所说(在4.3舍入方向属性下):

    除非另有说明,否则每次操作都应视为首次生产中间体结果以无限精度和无限范围进行校正,然后根据1对结果进行四舍五入条款中的属性

    对于除法,没有另行说明。因此,如果非零的a和b是相反的数字,那么a / b的无限精确结果是-1,并四舍五入到-1

  2. (最初)让我惊讶的是,它似乎在相反的方向上也是正确的,除非我错过了什么:如果是a / b == -1,那么是a == -b。要想失败,你需要接近的数字,除非有不同的符号。类似于(用Java术语)float b = -Math.nextDown(a)-Math.nextUp(a)的东西。但它们的指数在除法时会抵消(或几乎抵消2的幂),最终有效地得到a = 1b = -Math.nextDown(1),所以a / b == -1 / Math.nextDown(1) != 1。类似的逻辑适用于double

EDIT:正如Eric Postpischil的回答所示,我确实错过了一些东西,因为我假设了默认的舍入模式(和基数2,但我认为这更合理)。

如果非零a和b是相反的数字(?),这总是真的吗

并非总是如此。

当舍入模式从常见的FE_TONEAREST变为FE_TOWARDZEROFE_UPWARD时,我发现了商为-1但为a != -b的各种例子。

#include <float.h>
#include <limits.h>
#include <math.h>
#include <fenv.h>
#include <stdio.h>
void testf(float a, float b) {
if (b) {
float q = a / b;
//printf("%.9g %.9g %.9gn", a,b,q);
if (q == -1.0f && a != -b) {
printf("%.9g %.9g %.9gn", a, b, q);
}
}
}
int r(void) {
float f[] = {FLT_TRUE_MIN, FLT_MIN, 1, 2, FLT_MAX};
size_t n = sizeof f / sizeof f[0];
for (size_t ai = 0; ai < n; ai++) {
float a = nextafterf(f[ai], -FLT_MAX);
for (int aii = 0; aii < 3; aii++) {
for (size_t bi = 0; bi < n; bi++) {
float b = nextafterf(-a, -FLT_MAX);
for (int bii = 0; bii < 3; bii++) {
testf(a, b);
b = nextafterf(b, FLT_MAX);
}
}
a = nextafterf(a, FLT_MAX);
}
}
puts("Done");
return 0;
}
int main() {
fesetround(FE_DOWNWARD);
r();
fesetround(FE_TONEAREST);
r();
fesetround(FE_TOWARDZERO);
r();
fesetround(FE_UPWARD);
r();
}

输出

Done
Done
0.99999994 -0.999999881 -1
...
Done
0.99999994 -0.999999881 -1
...
1 -0.99999994 -1
...
1.99999988 -1.99999976 -1
...
Done

对于默认的舍入模式(即,向最接近的偶数关系舍入),如果a/b不是NaN,则该语句为true。

对于向零取整,这里有一个反例:

x = -2.717843e33
y =  2.7178426e33

对于IEEE754单精度浮点,以上两个值在向零取整时将产生-1;但是它们在绝对值上不相等。

相关内容

最新更新