倒数相乘是好还是坏?(这是一个关于IT技术的问题标题)

10

在处理 double 数据类型时,通过乘以倒数是更好还是更糟糕的?

哪种方法更? 哪种方法使用的内存更少? 哪种方法更受青睐?

MSIL 如何处理这个问题?

SquareInches = MMSquared / 645.16 
SquareInches = MMSquared * 0.0015500031000062000124000248000496

NB:10K用户会注意到这是这个问题的重复, 该问题被删除,因为最初的问题提问者决定在“评论”部分中责骂每一个人。

这个问题被重新发布,因为它是一个“好”的问题。

请取消“社区Wiki”,因为我只是将其发布为CW,以便它不被视为“声望” grab。

相关问题:

我应该使用乘法还是除法?


这个听起来很熟悉,我觉得它可能是一个重复的。 - Ali Afshar
如果有人能解释为什么这与@David Thornley发布的问题不同,我将重新开放此问题。 - Brian R. Bondy
公平地说,我只是转发了这个问题,所以我对此并没有任何立场;但是David Thornley的问题没有解决MSIL如何处理这个问题;而这个问题则有。从技术上讲,这不是一个重复的问题(至少不是在这个群体讨厌我“吹毛求疵”的情况下)。 - George Stocker
@Brian R Bondy:这不是答案的问题,而是问题本身的问题吗?还是应该更精确地提出问题? - George Stocker
@Gortok:哈哈,我是指你为什么要特别关注MSIL。从一个普通的回答中得不到什么MSIL特定的答案。无论如何,我要继续前进了。 - Brian R. Bondy
显示剩余28条评论
11个回答

20

使用逆矩阵进行乘法运算速度更快。编译器不会自动优化此操作,因为这可能会导致精度的小损失。(这实际上是 Walter Bright 经常访问的 D newsgroup 上讨论过的问题,并且他明确表示编译器不会自动执行此操作。) 通常应该使用除法,因为它更易读和准确。

如果你正在循环执行一段浮点代码超过十亿次 并且 你不在意轻微精度上的损失 并且 你将多次除以同一个数,那么通过逆矩阵进行乘法运算可以成为一个很好的优化方法。我确实在一些情况下通过逆矩阵乘法得到了显著的真实世界加速效果,但这些都是极端边缘情况,在执行了数十亿次的循环中几乎仅仅是对浮点数进行乘法运算而已。


4
一个简单的例子:光线追踪器需要对每个光线进行向量归一化或类似的操作(通常每个像素每帧需要多次)。 - schnaader
如果你正在进行乘法运算,因为你每秒要执行数十亿次,那么你可以添加一个注释来表示除法值,以提高可读性。 - Gloweye

11

哪个是"更快"的确是一个特定于CPU的问题,或者至少有多少更快是特定于CPU的,是的,除法通常被视为比乘法慢。当然,所有性能问题都可以用"取决于情况"来回答。

然而,如果你问哪一个 "更好" 而不是哪一个更快,那么就会有一个明确的答案:可读性更强的那个更好。您所寻求的性能提升可能在几个时钟周期的数量级上,因此,除非您要进行数百万次操作,否则您正在尝试节省微秒。没有任何微秒优化值得牺牲可读性和可维护性。


11

根据编译器和硬件的不同,这种优化可能会带来很少或者没有效果。

但是在紧凑的循环中,这仍然可能很重要,为了可读性,你应该写成:

SquareInches = MMSquared * (1 / 645.16)

最好使用常量来表示645.16。


乘法和除法怎么可能比其中任何一种更快呢? - lacop
4
因为每个编译器都会在编译时执行 (1 / 645.16) 的计算,所以这只是表示 0.0015...0496 更好的另一种方式。 - H H
哦,对了,没注意到。这绝对是更好的表示法,加一。 - lacop

5
答案取决于执行环境的架构。一般来说,在大多数处理器上,除法通常比乘法略微昂贵。因此,除非这实际上是一个性能问题,否则我可能不会担心它。选择更易理解的转换因子即可。

编译器不会对此进行优化,因为这可能会略微影响精度。 - dsimcha

4

从我的VB代码计时器

Dim SquareInches As Double
Dim MMSquared As Double = 81
Const d As Double = 645.16
Const m As Double = 1 / 645.16
Private Function TestCase1() As Boolean 'One
    'Test One Code Here
    SquareInches = MMSquared / d
    'end test code
    Return True
End Function
Private Function TestCase2() As Boolean 'Two
    'Test Two Code Here
    SquareInches = MMSquared * m
    'end test code
    Return True
End Function

结果

     3/17/2009 2:13:27 PM   CPU - 1.794GHz
 One - Using Division
 Two - Using Multiplication
 Outer Loops(OL)=7  Inner Loops=262,144
  ( times in ticks.  1 ms. = 10,000 ticks )
 >> Two faster, 0.0488 ticks/loop
  Ticks / Loop
 0.0342         0.0819          0.0331          0.0488
 OL Base        One             Two              One - Two
 1   8,936          21,459          8,609           12,850
 2   9,008          21,416          8,682           12,734
 3   8,965          21,423          8,643           12,780
 4   8,964          21,457          8,659           12,798
 5   8,966          21,469          8,640           12,829
 6   8,987          21,660          8,688           12,972
 7   8,963          21,429          8,802           12,627

  Average
 8,969          21,473          8,674           12,799
  Variance
 431.4          6,160.3         3,315.9       
  Standard Deviation
 20.8           78.5            57.6          
 3/17/2009 2:13:27 PM

作为相对度量,我想还可以。但作为时间的绝对度量,可能就不同了。这些时间来自于 Stopwatch 类。 - dbasnett

3

在大多数情况下,除法算法比乘法算法

这是一个权衡的问题,你可以选择更易读的方式或更快的方式。

// Using division operation
SquareInches = MMSquared / 645.16 

这种方式易于阅读和维护,但执行速度比其乘法计算方法慢:

// Using a multiplication 
SquareInches = MMSquared * 0.0015500031000062000124000248000496

如果你采用这种方法,需要更多的内存空间来存储倒数的位数,但算法运行速度会快得多。一位用户在 VS2005 项目上测试过它,并报告了乘法版本性能提高了8倍。
原因是乘法可以被盲目地转换为处理器上的移位和加法操作,这些是 CPU 上最优化的操作。一个好的带符号乘法算法是 Booth 算法(处理器会为你执行此操作)。另一方面,执行除法算法需要更多的控制开销,从而使除法算法变慢。
如果性能是你的需求,使用加法、减法(不过只是加上补码)、乘法、移位,但永远不要使用除法。如果你预先计算所有的倒数,并在一个除法密集型程序中使用它们进行乘法,你将获得一个显著的非可忽略的改进。

如果两个数字都转换为double,则它们使用相同的内存量。 - quant_dev

1
关于编译器执行多项式优化的问题,它们可以对此进行优化(GCC可以): SquareInches = MMSquared *(1 / 645.16)。

0
我认为第一种方法显然更好,因为它是明确的。想象一下在别人的代码中找到这个值。你怎么确定0.00155…真的等于1/645.16呢? 原始程序员如果出错怎么办?此外,我怎么知道645.16是正确的转换系数呢?最好不要将数字压缩或统一表示以简化处理。最基本的例子如下:
//hours per day * days per year
int hoursPerYear = 24*365;

我们可以清楚地看到这个数字是正确的,但是你怎么会知道8760是正确的答案呢?如果你需要执行许多这样的操作,你可能希望在进行密集计算之前将数据预处理为正确的形式。通过这种方式,你就不需要极高的效率,问题也就变得无关紧要了。

0

如果你正在除以像 645.16 这样的字面值,那么很可能没有区别,因为编译器可以轻松确定哪个版本更快并使用它。
如果你正在乘除一个变量,那么乘法可能会稍微快一些,因为逻辑通常更简单。
像任何事情一样,要确保,请使用分析器。


至少在x86-32上,GCC -O2不执行此类优化并生成fdivrs指令。这样的优化充满了潜在的陷阱,所以我怀疑许多编译器都不会这样做。 - TrayMan

0

乘法和加法是处理器支持的最快操作。一些处理器甚至没有除法、平方根等硬件实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接