我知道浮点数和 double 数的精度问题,这就是我问这个的原因:
如果我有一个公式,例如:(a/PI)*180.0
(其中 PI 是常数)
我应该把除法和乘法结合起来,所以我只能用一个除法:a/0.017453292519943295769236
,为了避免精度损失?
当计算结果的步骤较少时,这是否使它更精确?
最佳答案
简答
是的,您通常应该将尽可能多的乘法和除法组合成一个运算。它(通常(*))同时更快,更准确。
π 和 π/180 以及它们的倒数都不能精确地表示为浮点数。为此,计算将涉及至少一个近似常数(除了所涉及的每个操作的近似值)。
因为两个操作各自引入一个近似值,所以可以预期在一个操作中完成整个计算会更准确。
在手头的情况下,除法还是乘法更好?
除此之外,以浮点格式表示 π/180 的相对精度是比 180/π 好还是差是“运气”的问题。
我的编译器通过 long double
提供加法精度类型,所以我可以用它作为引用来回答 double
的这个问题:
~ $ cat t.c
#define PIL 3.141592653589793238462643383279502884197L
#include <stdio.h>
int main() {
long double heop = 180.L / PIL;
long double pohe = PIL / 180.L;
printf("relative acc. of π/180: %Le\n", (pohe - (double) pohe) / pohe);
printf("relative acc. of 180/π: %Le\n", (heop - (double) heop) / heop);
}
~ $ gcc t.c && ./a.out
relative acc. of π/180: 1.688893e-17
relative acc. of 180/π: -3.469703e-17
在通常的编程实践中,人们不会打扰并简单地乘以(浮点表示)180/π,因为乘法比除法快得多。
事实证明,在 binary64 浮点类型的情况下
double
几乎总是映射到,π/180 可以用比 180/π 更好的相对精度表示,所以 π/180 是应该用来优化精度的常数:a / ((double) (π / 180))
.使用此公式,总相对误差将近似为常数 (1.688893e-17) 的相对误差与除法的相对误差之和(取决于 a
的值,但永远不会超过2-53)。更快、更准确结果的替代方法
请注意,除法非常昂贵,您可以通过使用一次乘法和一次 fma 更快地获得更准确的结果: let
heop1
做最好的double
180/π 的近似值,以及 heop2
最好的double
180/π 的近似值 - heop1
.那么结果的最佳值可以计算为:double r = fma(a, heop1, a * heop2);
以上是绝对最好的事实
double
对真实计算的逼近是一个定理(实际上是一个有异常(exception)的定理。详细内容可以在《浮点运算手册》中找到)。但即使你想乘以一个 double
的实常数通过以获取 double
结果是定理的异常(exception)之一,上面的计算显然仍然非常准确,只是与最好的double
不同。 a
的几个异常值的近似值.如果像我一样,您的编译器为
long double
提供了更高的精度比 double
,您也可以使用一个 long double
乘法:// this is more accurate than double division:
double r = (double)((long double) a * 57.295779513082320876798L)
这不如基于 fma 的解决方案好,但对于
a
的大多数值来说已经足够了。 ,它产生最优 double
对实际计算的近似。操作应归为一个的一般主张的反例
(*) 最好将常数分组的说法仅在统计上对大多数常数是正确的。
如果您碰巧想乘以
a
例如,实常数 0.0000001 * DBL_MIN
,你最好先乘以 0.0000001
,然后通过 DBL_MIN
,并且最终结果(如果 a
大于 1000000 左右,则可以是归一化数字)将比乘以最好的 double
更精确。 0.0000001 * DBL_MIN
的表示.这是因为表示 0.0000001 * DBL_MIN
时的相对精度作为单例double
值比表示 0.0000001 的准确度差得多。
关于c++ - 在处理浮点值时,我应该结合乘法和除法步骤吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26655948/