我经常注意到gcc在可执行文件中将乘法转换为移位。当将int
与float
相乘时,可能会发生类似的情况。例如,2*f
可能只是将f
的指数增加1,从而节省一些周期。如果有人要求编译器这样做(例如,通过-fast-math
),编译器通常会这样做吗?
编译器通常是否足够聪明来执行此操作,还是我需要自己使用calb*()
或ldexp()/frexp()
函数系列来执行此操作?
常见的浮点格式,尤其是IEEE 754,不会将指数存储为简单的整数,并且将其视为整数不会产生正确的结果。
在32位浮点型或64位双精度型中,指数字段分别为8位或11位。指数代码1到254(浮点)或1到2046(双精度)的作用类似于整数:如果将一加到其中一个值上,结果是其中一个,则表示的值加倍。但是,在以下情况下添加一个失败:
(以上为正征兆。情况与负征兆对称。)
正如其他人所注意到的,一些处理器不具备快速处理浮点值的能力。即使在那些有隔离的位上,指数字段也没有与其他位隔离,所以在上面的最后一种情况下,通常不能在不溢出到符号位的情况下向它添加1。
尽管一些应用程序可以容忍忽略次法线或NaN甚至无穷大等快捷方式,但很少有应用程序可以忽略零。由于向指数添加1无法正确处理零,因此它不可用。
在现代 CPU 上,乘法通常具有每周期一个吞吐量和低延迟。如果该值已经在浮点寄存器中,则无法通过处理它来对表示进行整数算术来击败它。如果它一开始就在内存中,并且如果您假设当前值和正确结果都不是零,非正态,nan或无穷大,那么执行类似操作可能会更快
addl $0x100000, 4(%eax) # x86 asm example
乘以二;我唯一能看到这一点的好处是,如果你操作的是一个远离零和无穷大的整个浮点数据数组,并且按2的幂进行缩放是你唯一要执行的操作(因此你没有任何理由将数据加载到浮点寄存器中)。
例如,2*f可以简单地将f的指数增加1,从而节省一些周期。
这根本不是真的。
首先,你有太多的角落情况,如零,无穷大,Nan和非正规。然后你有性能问题。
误解是增加指数并不比乘法快。
如果查看硬件指令,则无法直接增加指数。因此,您需要做的是:
在整数和浮点执行单元之间移动数据通常存在中等到大的延迟。因此,最终,这种“优化”变得比简单的浮点乘法更糟糕。
所以编译器不做这种“优化”的原因是因为它一点也不快。
根据C编程语言-第4节,第6.2.5节: 有三种浮点类型:浮点(单精度)、双精度(双精度)和长双精度(扩展精度) 参考:http://en.wikipedia.org/wiki/Single-precision_floating-point_format 真有效位包括二进制点右侧的23个分数位和值为1的隐式前导位(二进制点左侧),除非指数存储为全零。因此,内存格式中只有23个有效位的分数位,但总精
考虑下面的代码片段 通过-O3优化,最新的gcc和clang版本没有优化指向包装器的指针、指向底层函数的指针。参见第22行的组装: 在后面的+中,编译器将指针放置到,而不只是。 编辑2。同样模式的更简单的例子: gcc 8.2通过抛出指向包装器的指针并将直接存储在其位置(https://gcc.godbolt.org/z/nmibeo)成功地优化了这段代码。然而,按照注释更改代码行(即手动执行相同
(这个问题与此密切相关,但它是一个更具体的问题,我希望能就此得到答案)
这样一个看似简单的数字怎么会“太大”而无法在64位内存中表达呢?
有问题的代码如下: 编译器会优化它吗?根据C-Standard(如果我理解正确的话),第二个操作数必须提升为;因此乘法必须使用FPU(或fp仿真)完成。 从理论上讲,该操作可以在正常的硬件寄存器中完成,只需添加一个即时的(并且可能是溢出检查)。是否允许编译器执行此优化?是否有已知的编译器这样做?如果是这样,他们是否也会识别该表达式 这是避免有关隐式转换的静态代码检查器警告所必需的? 补充一下:我知
问题内容: 根据 Swift编程语言 : 例如,0xFp2表示15×2 ^ 2,其值为60。类似地,0xFp-2表示15×2 ^(-2),其值为3.75。 为什么使用2作为指数的底数而不是16?我本来期望而不是 问题答案: Swift的十六进制浮点数表示法只是C99标准为输入和输出(使用printf 格式)为C引入的表示法的一种变体。 该表示法的目的是使人易于理解,并使IEEE 754表示法的位易