当前位置: 首页 > 知识库问答 >
问题:

在处理浮点值时,我是否应该结合乘法和除法步骤?

穆鸿飞
2023-03-14

我知道浮点数和双倍点数的精度问题,这就是为什么我要问这个问题:

如果我有一个公式,例如:(a/PI)*180.0(其中PI是常数)

我是否应该将除法和乘法结合起来,以便只能使用一个除法:a/0.017453292519943295769236,以避免精度损失?

当计算结果的步骤较少时,这是否会使其更精确?


共有1个答案

高明辉
2023-03-14
匿名用户

是的,您通常应该将尽可能多的常量乘法和除法组合到一个操作中。它(通常(*)同时更快、更准确。

π和π/180及其逆都不能完全表示为浮点数。因此,计算将涉及至少一个近似常数(除了所涉及的每个操作的近似之外)。

因为两个操作各引入一个近似,所以可以预期在一个操作中进行整个计算会更准确。

除此之外,用浮点格式表示π/180的相对精度是优于还是低于180/π,这是一个“运气”的问题。

我的编译器为长双精度类型提供了加法精度,因此我可以使用它作为回答双精度问题的参考:

~ $ 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/((双) (π / 180))。使用此公式,总相对误差将近似为常数(1.688893e-17)的相对误差和除法的相对误差(这将取决于a的值,但永远不会超过2-53)。

请注意,除法非常昂贵,使用一次乘法和一次fma可以更快地获得更精确的结果:让heop1是180/π的最佳近似值,heop2是180/π的最佳近似值。然后,结果的最佳值可以计算为:

double r = fma(a, heop1, a * heop2);

上述是对真实计算的绝对最佳可能的近似这一事实是一个定理(事实上,它是一个有例外的定理。详细信息可以在“浮点算术手册”中找到)。但即使当您想要将乘以以以获得结果的真实常数是定理的例外之一时,上述计算仍然显然非常准确,并且仅与a的少数异常值的最佳近似不同。

如果像我一样,您的编译器为long Double提供的精度比ple更高,您还可以使用一个long Double乘法:

// this is more accurate than double division:
double r = (double)((long double) a * 57.295779513082320876798L)

这不如基于fma的解决方案好,但它足够好,对于大多数值a,它可以产生与实际计算的最佳双近似值。

(*)对常数进行分组更好的说法仅在统计上适用于大多数常数。

如果您碰巧希望将a乘以(例如)实常数0.0000001*,那么最好先乘以0.0000001,然后再乘以DBL\u MIN,最终结果(如果a大于1000000左右,则可以是一个标准化数字)将比乘以0.0000001*DBL\u MIN的最佳表示形式更加精确。这是因为将0.0000001*DBL\u MIN表示为一个双值时的相对精度比表示0.0000001的精度差得多。

 类似资料:
  • 众所周知,除法比乘法需要更多的时钟周期来计算。(请参阅此处的讨论:浮点除法与浮点乘法。) 我已经在我的C代码中使用了< code>x * 0.5而不是< code>x / 2和< code>x * 0.125而不是< code>x / 8,但是我想知道我应该这样做到什么程度。 对于倒置时重复出现的小数(即是重复出现的十进制),我使用除法而不是乘法(例如而不是)。 我的问题是:在迭代次数相当大的循环

  • 问题内容: 我知道该ValueEventListener线程在新线程中运行,是否应该在任何时间删除此线程以进行适当的线程管理?(例如,没有太多并行运行的线程)。如果是,该怎么办? 问题答案: 在谈论监听器时,是的,您需要根据活动的生命周期将其删除,为此,您需要使用以下代码行: 请记住,如果不这样做,最终将浪费您的电池和带宽。所以: 如果已在中添加了侦听器,则onStart必须在中将其删除onSto

  • 问题内容: 5年前关闭。 我必须将两个整数相除并得到一个浮点数作为我的代码: 我用调试器检查值 为什么结果为0.0?我应该怎么做才能获得正确的浮动? 问题答案: 当您将两个数相除时,将执行整数除法,在这种情况下,将导致22/64 =0。只有完成此操作后,您才能创建一个。和的表示是。如果要执行浮点除法,则应 在 除法 之前进行 强制转换:

  • 关于尾数(关于浮点运算的指南),实际上如何将两个尾数相乘? 假设IEEE 754单精度浮点表示。 假设一个数字的尾数为,将被编码为(十进制为)。第二个数字的尾数为,将被编码为(十进制为)。 <代码>1.5 x 1.125=1.6875。 编码为(十进制为)。但是不等于... 尾数乘法是如何工作的,以至于将4194304(1.5)乘以1048576(1.125),得到5767168(1.6875)?

  • 我有多个线程使用ElasticSearchClient,如下所述 据我所知,Singleton类是线程安全的,但我不确定如果多个线程开始执行Singleton类的同一方法会发生什么。这有副作用吗? 注意:我知道上面的singleton类不是反射和序列化安全的。

  • 因此,我有一个Javascript脚本,它在一个循环中将小的分数相加,它有可能将0.2加到0.1。然后,这个值被输入到另一个函数,但问题是,我需要0.3来精确输入,而不是0.3000000000000004。 什么是最简单的方法,以确保数字是正确和准确的。注意,它可能得到0.25+0.125等,被添加到简单的四舍五入到小数点1不会解决问题。 也有可能添加0.2+0.1000000000000000