据我了解,浮点存储在XMM
寄存器中,而不是{eax
这样的通用寄存器中,因此我做了一个实验:
float a = 5;
在这种情况下,a
作为1084227584
存储在XMM
寄存器中。
这是程序集版本:
.text
.global _start
.LCO:
.long 1084227584
_start:
mov .LCO, %eax
movss .LCO, %xmm0
执行上述程序集并使用gdb
对其进行调试,表明eax
中的值为1084227584
,但是ymm0
中的值为5
。
这是我的问题:
1- XMM
寄存器有什么特别之处?除了SIMD指令,它们是唯一存储浮点的寄存器类型吗?
为什么不能在常规寄存器中设置相同的位?
2- float
和double
值是否总是存储为浮点数?
我们永远不能将它们作为fixed point
存储在C或程序集中吗?
答案 0 :(得分:6)
但是ymm0中的值为5。
ymm0中的位模式为1084227584
。该数字的浮点型解释为5.0
。
但是您可以print /x $xmm0.v4_int32
来查看xmm0中位的十六进制表示。
XMM寄存器有何特别之处?除了SIMD指令之外,它们是存储浮点数的唯一寄存器类型吗?
不,在asm中,一切都只是字节。
某些编译器将使用整数寄存器将浮点数或双精度数从一个内存位置复制到另一个内存位置,如果不对其进行任何计算的话。 (整数指令通常较小)。铛会这样做:https://godbolt.org/z/76EWMY
void copy(float *d, float *s) { *d = *s; }
# clang8.0 -O3 targeting x86-64 System V
copy: # @copy
mov eax, dword ptr [rsi]
mov dword ptr [rdi], eax
ret
XMM / YMM / ZMM寄存器是特殊的,因为它们是FP ALU指令存在的唯一寄存器(忽略x87,x87仅用于x86中的80位long double
-64)。
addsd xmm0, xmm1
(加标量双精度)没有等效于整数寄存器。
通常FP和整数数据不会相交太多,因此提供一组完整的体系结构寄存器可以为寄存器中的更多数据提供更多空间。 (鉴于相同的指令编码约束,可以在16 FP + 16 GP整数与16个统一寄存器之间进行选择,而不是对32个统一寄存器之间进行选择。)
另外,单独的寄存器文件的主要微体系结构优势在于,它在物理上可以接近FP ALU,而整数寄存器文件在物理上可以接近整数ALU。 有关更多信息,请参见Is there any architecture that uses the same register space for scalar integer and floating point operations?
float
和double
值是否总是存储为浮点数?我们永远不能将它们存储为C或汇编中的固定点吗?
x86编译器使用float
= IEEE754 binary32 https://en.wikipedia.org/wiki/Single-precision_floating-point_format。 (并且double
= IEEE754 binary64)。这被指定为ABI的一部分。
在内部,如果最终规则相同,则as-if规则允许编译器执行所需的任何操作。 (或者使用-ffast-math
,以假装FP数学是关联的,并假设NaN / Inf不可能。)
编译器不能只是为其他单独编译的函数可能会查看的float
随机选择其他对象表示形式。
对于本地函数来说,在其他函数永远看不见的情况下,极少数情况下,“人工编译器”(实现C的手写asm)可以证明定点是安全的。或更可能的是,float
的值是足够小的精确整数,以至于double
不会将它们取整,因此您的定点值可以退化为整数(可能是最后一步)。
但是很少知道可能的值而不仅仅是能够进行恒定传播并优化所有内容。这就是为什么我说必须要有人参与,以证明编译器不知道要寻找的东西。
我认为从理论上讲,您可以有一个C实现,使用 使用定点float
或double
。 ISO C非常适合对float
和double
的实际用途没有什么限制。
但是limits.h
constants like FLT_RADIX
and DBL_MAX_EXP
所具有的交互作用对于定点格式可能没有意义,该定点格式在每个可表示值之间具有恒定的距离,而不是在0附近彼此靠得很近,而对于较大数字则彼此靠得很远。 (0.5ulp的舍入误差是相对于幅度的,而不是绝对的。)
尽管如此,如果“尾数”和指数限制不符合您对DBL_MIN
和DBL_MAX
的期望,大多数程序实际上并不会做会破坏的事情。
另一个有趣的可能性是基于Posit格式制作float
和double
(类似于传统的浮点,但具有可变长度的指数编码。https://www.johndcook.com/blog/2018/04/11/anatomy-of-a-posit-number/ {{3 }}。
现代硬件,尤其是Intel CPU,对IEEE浮点/双精度有很好的支持,因此定点通常不是一个好选择。不过,对于16位定点有一些不错的SIMD指令,例如仅上半数乘法,甚至还有https://posithub.org/index都可以进行定点舍入。
但是,一般的32位整数乘法比打包float
乘法的吞吐量要差。 (因为针对浮点/双精度进行了优化的SIMD ALU每32位向量元素仅需要24x24位有效乘数。现代Intel CPU在FMA执行单元上运行整数乘法和移位,每个时钟吞吐量为2 oups。)
答案 1 :(得分:2)
它们是唯一存储浮点数的寄存器吗?
不。兼容8087的FPU中有80位浮点寄存器(fp0
-fp7
),在大多数现代CPU中仍应存在。
大多数32位程序都使用这些寄存器。
我们可以在常规的 [integer] 寄存器中存储浮点吗?
是的。 30年前,许多PC都包含一个没有80x87 FPU的CPU,因此没有fp0
-fp7
寄存器。具有XMM寄存器的CPU甚至更晚了。
今天我们在移动设备中发现了类似的情况。
XMM寄存器有什么特别之处?
使用80x87 FPU似乎比使用XMM寄存器更为复杂。此外,我不确定每个操作系统的64位程序中是否允许使用80x87。
如果将浮点值存储在整数寄存器(例如eax
)中,则没有任何指令执行算术运算:在x86 CPU上,没有用于执行乘法或加法运算的指令。存储在整数寄存器中的浮点值。
对于没有FPU的CPU,您必须进行浮点仿真。这意味着您必须通过执行多个整数运算来执行一个浮点运算,就像使用纸和铅笔一样。
但是,如果只想存储浮点值,则当然也可以使用整数寄存器。复制一个值或检查两个值是否相等以及相似的操作也是如此。
我们永远不能将它们存储为C或汇编中的固定点吗?
在使用没有FPU的CPU时,经常使用固定点。
例如,当使用仍在汽车工业,消费类设备或PC外围设备中使用的8位或16位CPU时。
但是,我怀疑是否有C编译器会自动将关键字“ float
”转换为定点。