我应该使用乘法或除法来重复浮点数吗?

时间:2016-02-28 23:55:25

标签: c++ floating-point

众所周知,除了乘法,除法需要更多的时钟周期。 (请参阅此处的讨论:Floating point division vs floating point multiplication。)

我已经在我的C ++代码中使用x * 0.5代替x / 2x * 0.125而不是x / 8,但我想知道我应该采取多远措施。

对于反转时重复出现的小数(即。1 / num是重复的十进制数),我使用除法而不是乘法(例如x / 2.2而不是x * 0.45454545454)。

我的问题是:在迭代次数相当多的循环中,我应该用它们的重复乘法对应物替换除数(即。x * 0.45454545454而不是x / 2.2) ,或者这会带来更大的精度损失吗?

编辑:我做了一些分析,我在Visual Studio中启用了完全优化,使用了Windows QueryPerformanceCounter()函数来获取分析结果。

int main() {
    init();
    int x;
    float value = 100002030.0;
    start();
    for (x = 0; x < 100000000; x++)
        value /= 2.2;
    printf("Div: %fms, value: %f", getElapsedMilliseconds(), value);
    value = 100002030.0;
    restart();
    for (x = 0; x < 100000000; x++)
        value *= 0.45454545454;
    printf("\nMult: %fms, value: %f", getElapsedMilliseconds(), value);
    scanf_s("");
}

结果为:Div:426.907185ms,值:0.000000                  Mult:289.616415ms,值:0.000000

除了优化之外,除法几乎是乘法的两倍。性能优势得到保证,但它们会降低精度吗?

2 个答案:

答案 0 :(得分:7)

  

对于反转时重复出现的小数(即1 / num是重复的小数),我使用除法而不是乘法(例如x / 2.2而不是x * 0.45454545454)。

众所周知,22/10在二进制浮点中无法准确表示,因此您所实现的只是稍微不准确的值,而不是乘以稍微不准确的值。

实际上,如果意图是除以22/10或其他一些不一定在二进制浮点中可以完全表示的实数值,那么一半时间,乘法比除法更准确,因为它巧合的是,1 / X的相对误差小于X的相对误差。

另一个评论是你的微基准测试会遇到次正规数,其中时间不能代表正常浮点数上常规操作的时间,并且在一段时间后,value为零,再次意味着时间不能代表正常数字乘法和除法的现实。正如Mark Ransom所说,至少应该使两个测量的操作数相同:因为当前写入的所有乘法都采用零操作数并导致零。此外,由于2.20.45454545454都有类型double,因此您的基准测试是测量双精度乘法和除法,如果您愿意通过双精度实现单精度除法乘法,这是needs not involve any loss of accuracy(但您必须为1/2.2提供更多数字。)

但是不要让自己被愚弄试图修复微观基准。 你不需要它,因为当X不能比1 / X更准确地表示时,没有权衡。没有理由不使用乘法。

注意:您应该明确地乘以1 / X,因为由于两个操作/ X* (1 / X)略有不同,编译器本身无法进行替换。另一方面,您不需要将/ 2替换为* 0.5,因为任何有价值的编译器都应该为您执行此操作。

答案 1 :(得分:1)

当乘以倒数与除数时,您将获得不同的答案,但在实践中,它通常无关紧要,并且性能增益是值得的。最多,误差将是1 ULP用于倒数乘法,而½ULP用于除法。但是

a = b * (1.f / 7.f);

而不是

a = b * 0.142857f;

因为前者将为1/7生成最准确的(½ULP)表示。