关于尾数(re this guide on floating point arithmetic),你如何实际将两个尾数相乘?
假设IEEE 754单精度浮点表示。
假设一个数字的尾数为1.5
,其编码为0b10000000000000000000000
(十进制为4194304
)。第二个数字的尾数为1.125
,其编码为0b00100000000000000000000
(十进制为1048576
)。
1.5 x 1.125 = 1.6875
。
1.6875
编码为0b10110000000000000000000
(十进制为5767168
)。但4194304 * 1048576
不等于5767168
...
尾数乘法如何使4194304
(1.5)乘以1048576
(1.125),得到5767168
(1.6875)?
也就是说,你如何将编码的尾数相乘?
即。硬件如何使用存储在内存中的编码尾数来获取新值?
从the guide开始,浮点数的乘法可以如下实现。我被困在第2步。
- 将尾数与指数分开
- 将尾数部分乘以(或除以)
- 一起添加(或减去)指数
- 将两个结果合并为新值
- 规范化结果值(可选)
醇>
答案 0 :(得分:0)
您想要产品(1.0 * 2 -1 )*(1.0 * 2 -3 )。
尾数是1和1,指数是-1和-3。
因此尾数的乘积为1 * 1 = 1,指数之和为(-1)+( - 3)= -4。
因此,根据步骤1至4,两个因子的乘积为1 * 2 -4 = 0.0625。
要记住两个重要的事项:(1)二进制IEEE 754浮点中的指数总是表示2的幂,以及(2)归一化尾数的整数部分总是1(因此第一位可以隐藏在数字的实际二进制表示中。
编辑:自问题被重写后,将我的一些评论纳入此答案。
您链接的指南似乎描述了行为,但没有描述实施。如果实现如此简单,我认为浮点数学将与整数数学一样快。
对于尾数,硬件可能会做某些事情,例如在某个时刻添加回隐藏位,这样就可以了
0b110000000000000000000000 * 0b100100000000000000000000 = 0b11011000000000000000000000000000000000000000000
在删除隐藏位并舍去尾随位后,这将变为0b10110000000000000000000。
对于指数,硬件可以从两个因子的偏差指数中减去127,执行算术,然后将127加回到求和或差值以重新偏置指数。
请记住,64位格式用于对内存中的数字进行紧凑编码,但这可能不是执行数学时使用的实际表示形式。处理器甚至可以以80位精度执行中间数学运算,然后在将值写回存储器时将结果舍入为64位。请参阅x87 80-bit double-extended precision。
答案 1 :(得分:0)
它的工作原理与小学数学相似,因为你只需知道你的乘法表,并记住任何时候零都是零。
让我拿1.5 * 3.0,单精度。
0x3FC00000 1.5
0x40400000 3.0
0x40900000 1.5 * 3.0 = 4.5
扩展
0x3FC0
0011111111000000
0 01111111 1000000
0x4040
0100000001000000
0 10000000 1000000
所以在二进制中我们乘以1.1乘以1.1,就像小学一样,除了更容易
11
* 11
======
11
+11
======
1001
然后我们从一个操作数和另一个操作数中有一个(非)十进制(二进制?)位置,所以我们的句点从右边开始有两个。
10.01
但我们需要规范化
1.001 with an increase in the exponent.
指数
01111111 2 ^ 0
10000000 2 ^ 1
就像小学一样,我们添加指数2 ^(0 + 1)= 2 ^ 1。然后我们将另一个移位加1到指数2 ^(1 + 1)= 2 ^ 2进行归一化。
0 10000001 001000....
010000001001000....
0100 0000 1001 000....
给出与计算机生成的结果相匹配的结果
0x40900000
没有魔力。
硬件中的乘法与小学没有什么不同,它只是更简单。查看这些单独的位a是0或1位,b是位0还是1,依此类推。
ab
* cd
======
ef
gh
======
jklm
如果d为0,则如果d为1,则ef均为零,则ef = ab 所以到目前为止的方程是
e = a & d
f = b & d
g = a & c
h = b & c
任何以0结尾的东西都是0,任何以1结尾的东西本身就是。
然后我们做补充
nop0
ef
+ gh
======
jklm
我在这里做了一些重作弊两位加,执行和结果,真值表
xy cr
00 00
01 01
10 01
11 10
结果为x或y,执行为x和y
m = f
p = 0 because f+0 cant have a carry bit.
l = e xor h
o = e & h
k = o xor g
n = o & g
j = n
替换,希望我不会犯任何(更多)错误
m = f = b & d
l = e xor h = (a&d) xor (b&c)
k = o xor g = (e & h) xor (a&c) = ((a&d) & (b&c)) xor (a&c)
j = n = o & g = (e & h) & (a&c) = ((a&d) & (b&c)) & (a&c)
因此可能存在一些优化,但如果您需要/想要在一个时钟周期内计算两位乘二位乘法器,那么就有输入和输出方程式。很多作弊,因为做3x3而且加法变得更糟。按照8位或8位或32位的方式进行工作.B。在其他进位位上开始进行垃圾邮件,数学运算不是您想要手动尝试的。这是可能的,但是会产生大量的逻辑,单个时钟乘法器会占用你芯片的很大一部分,有一些技巧......使用一个以上的时钟和一个管道给人一种时钟的错觉......
回到过去的日子里,我们根本无法燃烧那么多的逻辑,我们会说零累加器,然后取ab位和两者,d左移零,然后加到累加器。取b位并且两个都向左移动c并添加到累加器......就像我们做纸和铅笔乘法一样。如果你有一个8位* 8位,则只需要乘法至少8个时钟。然后归一化等等......
无论如何,你可以看到逻辑上的乘法与我们在小学时学到的相比没有什么不同,因为我们只是乘以0和1.你可以写零或按原样复制操作数。
简短的回答,你可能错过了隐藏/暗示位的观点,没有理由浪费一点可用于精确的格式。当标准化时,IEEE 754对于某些功率是1.mantissa。
双重相同的故事,暗示1.mantissa:
0x3FF8000000000000 1.5
0x4008000000000000 3.0
0x4012000000000000 4.5
0011111111111000
0100000000001000
0100000000010010
0 01111111111 1000 1.1000...
0 10000000000 1000 1.1000...
0 10000000001 0010 1.0010....
另一种看待这个没有小数点的方法(或暗示它们并且它们不是小数点,而是我猜的二进制点)。
从1.10000以上...... * 1.10000是0xC00000 * 0xC00000 = 0x900000000000,48位。我们将一半的位剥离到0x900000,因为我们有一个1位和47位的尾数,但只有23位的空间,所以我们切断了24位低位,无论是零还是任何其他数字。我们保留了一半的比特并丢弃了一半的比特,这并不是偶然的。现在它已经是1.000 ... * 1.000 ......我们将有0x400000000000 1和46位斩波23而不是24位。您可以使用较少的位数进行一些实验,但请记住,与任何两个相互重叠的N位数字不同,我们在顶部有一个隐含/固定1。所以(1 * 2 ^ 23)*(1 * 2 ^ 23)=(1 * 1)*(2 ^(23 + 23)= 1 * 2 ^ 46总是在尾数乘以时(正常,非 - 零,数字)。
还有其他浮点格式在大多数情况下都是相同的,它们可能更容易用于负数等等(例如,如果你能找到它,请查看ti DSP格式,为速度而不是极端情况而构建)。
答案 2 :(得分:-1)
我发现this video回答了我的问题。
要将编码值相乘,硬件会以某种方式忽略拖尾零。还放置了一个前导位。
因此,对于1.5,使用0b11
(3)代替0b10000000000000000000000
(4194304)。
同样,对于1.125,使用0b1001
(9)代替0b00100000000000000000000
(1048576)。
因此0b11 * 0b1001 = 0b11011
(27)。
如果忽略0b11011
中的额外前导位*并添加尾随零,则以0b10110000000000000000000
结尾(5767168)。
* around 8:05解释了重要位的重要内容。对于这个例子,可以忽略额外的前导一位就足够了。