平方差的数值精度

csc*_*wan 8 c c++ numerical-analysis

在我的代码中,我经常计算类似下面的内容(这里为简单的C代码):

float cos_theta = /* some simple operations; no cosf call! */;
float sin_theta = sqrtf(1.0f - cos_theta * cos_theta); // Option 1
Run Code Online (Sandbox Code Playgroud)

对于此示例,请忽略由于不精确而导致平方根的参数可能为负.我通过额外的fdimf电话修好了.但是,我想知道以下是否更精确:

float sin_theta = sqrtf((1.0f + cos_theta) * (1.0f - cos_theta)); // Option 2
Run Code Online (Sandbox Code Playgroud)

cos_theta介于两者之间-1,+1因此对于每种选择都会有一些情况,我会减去相似的数字,因此会失去精度,对吧?什么是最精确的,为什么?

1''*_*1'' 4

使用浮点数的最精确方法可能是使用单个 x87 指令fsincos来计算 sin 和 cos 。

但是,如果您需要手动进行计算,最好将具有相似大小的参数分组。这意味着第二个选项更精确,尤其cos_theta是当 接近 0 时,此时精度最重要。

正如《每个计算机科学家应该了解浮点算术》一文 所述:

表达式 x 2 - y 2是另一个表现出灾难性抵消的公式。将其评估为 (x - y)(x + y)更为准确。

编辑:它比这更复杂。虽然上述内容通常是正确的,但当 x 和 y 的大小相差很大时, (x - y)(x + y) 的准确度稍差,如该声明的脚注所解释的那样:

在这种情况下,(x - y)(x + y) 有三个舍入误差,但 x 2 - y 2只有两个舍入误差,因为计算 x 2和 y 2中较小者时产生的舍入误差不会影响最终的减法。

换句话说,取 x - y、x + y 以及乘积 (x - y)(x + y) 都会引入舍入误差(舍入误差的 3 步)。x 2、 y 2和减法 x 2 - y 2也各自引入舍入误差,但是通过对相对较小的数(x 和 y 中较小的一个)进行平方获得的舍入误差可以忽略不计,因此实际上只有两个步骤舍入误差,使平方差更加精确。

所以选项 1 实际上会更精确。dev.brutus 的 Java 测试证实了这一点。