我经常注意到gcc将乘法转换为可执行文件中的移位。将int
和float
相乘时可能会出现类似情况。例如,2 * f
可能只是将f
的指数递增1,从而节省了一些周期。编译器,也许是一个人请求他们这样做(例如通过-ffast-math
),一般来说,这样做吗?
编译器通常是否足够聪明,或者我是否需要使用scalb*()
或ldexp()/frexp()
函数系列自行完成此操作?
答案 0 :(得分:81)
例如,2 * f,可能只是将f的指数递增1, 节省一些周期。
这根本不是真的。
首先你有太多的角落情况,如零,无穷大,楠和非正规。然后你就遇到了性能问题。
误解是增加指数并不比进行乘法更快。
如果查看硬件指令,则无法直接增加指数。 所以你需要做的是:
在整数和浮点执行单元之间移动数据通常存在中到大延迟。所以最后,这种“优化”变得比简单的浮点乘法更糟糕。
因此,编译器不执行此“优化”的原因是因为它没有任何更快。
答案 1 :(得分:20)
在现代CPUs上,乘法通常具有每周期一个吞吐量和低延迟。如果该值已经存在于浮点寄存器中,则无法通过对其进行处理以对表示进行整数运算来击败它。如果它在内存中开始,并且如果你假设当前值和正确的结果都不是零,非正规,纳米或无穷大,那么它可能更快地执行类似< / p>
addl $0x100000, 4(%eax) # x86 asm example
乘以2;唯一一次我能看到这是有益的,如果你在一系列浮点数据上操作,这些浮点数据与零和无穷大有限,并且以2的幂为单位缩放是你将要执行的唯一操作(所以您没有任何现有理由将数据加载到浮点寄存器中。)
答案 2 :(得分:18)
常见的浮点格式,特别是IEEE 754,不会将指数存储为一个简单的整数,并将其视为整数将不会产生正确的结果。
在32位浮点数或64位双精度数中,指数字段分别为8或11位。指数代码1到254(浮点数)或1到2046(双精度数)的行为类似于整数:如果您将这些值中的一个添加到其中一个并且结果是这些值之一,则表示的值会加倍。但是,在这些情况下添加一个失败:
(以上是积极的迹象。情况是对称的,有负面的迹象。)
正如其他人所指出的,某些处理器没有快速操作浮点值位的工具。即使在这样做的情况下,指数字段也不会与其他位隔离,因此您通常无法在上一个情况下添加一个而不会溢出到符号位。
虽然某些应用程序可以容忍快捷方式,例如忽略次正规或NaN甚至无穷大,但应用程序很少会忽略零。由于向指数添加一个无法正确处理零,因此无法使用。
答案 3 :(得分:9)
这不是编译器或编译器编写者不聪明。它更像是遵守标准并产生所有必要的“副作用”,如Infs,Nans和denormals。
也可能是不产生其他未被要求的副作用,例如读取内存。但我确实认识到在某些情况下会更快。
答案 4 :(得分:3)
实际上,这就是硬件中发生的事情。
2
也作为浮点数传递给FPU,尾数为1.0,指数为2 ^ 1。对于乘法,增加了指数,并且尾数乘以。
鉴于存在专用硬件来处理复杂的情况(乘以不是2的幂的值),并且特殊情况的处理不比使用专用硬件更差,没有必要额外增加电路和说明。
答案 5 :(得分:0)
嵌入式系统编译器可能有用,它具有特殊的2-power-of-2伪操作,可以由代码生成器以任何对所讨论的机器最佳的方式进行转换,因为在某些嵌入式处理器上专注于指数可能比执行完全二次幂乘法快一个数量级,但在乘法最慢的嵌入式微处理器上,编译器可能通过浮点乘法例程实现更大的性能提升在运行时检查它的参数,以便跳过尾数的零部分。
答案 6 :(得分:0)
A previous Stackoverflow question about multiplication by powers of 2。共识和实际实现证明,遗憾的是,目前没有比标准乘法更有效的方法。
答案 7 :(得分:0)
如果您认为乘以2意味着将指数增加1,请再想一想。以下是IEEE 754浮点运算的可能情况:
案例1:无限和NaN保持不变。
情况2:通过增加指数并将尾数除了符号位设置为零,将具有最大可能指数的浮点数更改为无穷大。
情况3:指数小于最大可能指数的归一化浮点数的指数增加1。 YIPPEE !!!
情况4:具有最高尾数位集的非正规化浮点数将其指数增加1,将它们转换为标准化数字。
情况5:清除最高尾数位的非正规化浮点数,包括+0和-0,其尾数向左移位一位,使指数保持不变。
我非常怀疑生成整数代码的编译器能够正确处理所有这些情况,并且处理器中内置的浮点数就会快。并且它仅适用于乘以2.0。对于乘以4.0或0.5,适用一整套新规则。对于乘以2.0的情况,您可能会尝试用x + x替换x * 2.0,并且许多编译器都会这样做。也就是说他们这样做,因为处理器可能能够同时进行一次加法和一次乘法,但不是每种加法。所以有时你更喜欢x * 2.0,有时候更喜欢x + x,这取决于其他操作需要同时做什么。