二进制定点乘法
Binary fixed point multiplication
我正在实现一个 VHDL 8 位定点乘法模块,其中 returns 一个 8 位截断数,但是当我手动进行乘法以测试它时遇到了问题。当我想将两个负数相乘时,问题就出现了。
我尝试将两个正值相乘 1.67 * 0.625 ~ 1.04(二进制乘法为 0.906)。
001.10101 -> 1.67
000.10100 -> 0.625
------------
000000.1110111010 = 000.11101 (truncated to 8bits = 0.906)
我试过将负数和正数相乘 (-0.875 * 3 ~2.62)
111.00100 -> -0.875
011.00000 -> 3
----------
010101.0110000000 = 101.01100 (truncated to 8bits = -2.625)
到目前为止一切正常。当我尝试将两个负数相乘时,问题就来了。据我所知(除非我记错了):
- 将两个数字相乘将得到两倍分辨率的结果(将两个 8 位数字相乘得到一个 16 位数字)
- 固定点也会错位。在此示例中,固定点之前有 3 位,之后有 5 个点。这意味着在结果数中,定点将在点之前有 6 位数字,在点之后有 10 位。
假设上述计算正确。但是当我尝试将两个负值相乘时 (-0.875 * -1.91 ~ 1.67)
110.00010 -> -1.91 (1.9375)
111.00100 -> -0.875
------------
101011.0011001000 = 011.00110(truncated to 8 bits = 3.1875)
当然,我尝试了另一个负乘法(-2.64 * -0.875 = 2.31)
101.01011 -> -2.64
111.00100 -> -0.875
----------
100110.0001001100 = 110.00010 (truncated to 8bits = -1.9375)
很明显我做错了什么,但我就是看不出我做错了什么。
PS:我还没实现呢。我想到了我要怎么做,然后我尝试用一些简单的例子手工测试它。而且我还尝试了更多的乘法。我想也许他们成功了,因为我很幸运,但显然不是,我又尝试了几次乘法,他们成功了。所以也许我在将两个负数相乘时做错了什么,也许我把它截断了?大概。
编辑:
好的,我找到了一份 Xilinx 文档,其中说明了当两个操作数为负时如何进行乘法运算,here 就是 link。根据这个文件,为了这个文件,这只能在做扩展乘法时完成。并且乘法的最后部分和必须取反,然后加 1 才能得到正确的数字。
为了乘法,我在程序员模式下使用了 windows' 计算器,这意味着为了乘以 8 位,我将数字放入计算器,然后得到结果并将其截断。如果它们适用于其他情况,则意味着 windows 计算器正在执行直接乘法(按应有的方式添加所有部分和,而不是反转最后的部分和)。所以,这意味着为了获得真正的结果,我应该从最终结果中减去第一个操作数,然后加上第一个操作数 inverted + 1
110.00010 -> -1.91 (1.9375)
111.00100 -> -0.875
------------
101011.0011001000
Which gave me the result: 000010.0111001000 = 010.01110(truncated to 8bits =2.43)
与另一个我得出的结果是 1.875。这些输出不是很好,但至少它们更接近我的预期。还有其他更简单的方法吗?
您的中间结果是错误的,因此截断没有按预期进行。此外,如果中间结果的四个 top-most 位在您的格式中相等,则只有在没有溢出的情况下才能截断。
您应该使用带符号的 data-types 进行正确的乘法运算。
即使你的第二个例子也是错误的。中间二进制结果 010101.0110000000
代表十进制数 21.375,它不是 -0.875 和 3 的乘积。所以,让我们手工做乘法:
a * b = -0.875 * 3 = -2.625
111.00100 * 011.00000
---------------------
. 00000000 // further lines containing only zeros have been omitted
+ .01100000
+ 011.00000
+ 0110.0000
+ 110100.000 // add -(2^2) * b !
= 111101.0110000000 = -2.625 (intermediate result)
= 101.01100 = -2.625 after truncation
你必须在最后的部分和中添加 b
的二进制补码,因为 a
的 top-most 位中的 '1' 表示值 -(2^2 ) = -4。此处可以进行无溢出截断,因为中间结果的 4 top-most 位相等。
现在是第三个例子
a b = -1.9375 * -0.875 = 1.6953125
110.00010 * 111.00100
---------------------
. 00000000 // further lines containing only zeros have been omitted
+ 111111.111100100 // sign-extended partial-sum
+ 111110.0100 // sign-extended partial-sum
+ 000011.100 // add -4 * b
= 000001.101100100 = 1.6953125 (intermediate result)
~ 001.10110 = 1.6875 after truncation
由于b
是一个带符号的数字,所以总是sign-extend中间结果宽度的部分和。当然,在第二个例子的计算中也这样做了,但是没有区别。
我正在实现一个 VHDL 8 位定点乘法模块,其中 returns 一个 8 位截断数,但是当我手动进行乘法以测试它时遇到了问题。当我想将两个负数相乘时,问题就出现了。
我尝试将两个正值相乘 1.67 * 0.625 ~ 1.04(二进制乘法为 0.906)。
001.10101 -> 1.67
000.10100 -> 0.625
------------
000000.1110111010 = 000.11101 (truncated to 8bits = 0.906)
我试过将负数和正数相乘 (-0.875 * 3 ~2.62)
111.00100 -> -0.875
011.00000 -> 3
----------
010101.0110000000 = 101.01100 (truncated to 8bits = -2.625)
到目前为止一切正常。当我尝试将两个负数相乘时,问题就来了。据我所知(除非我记错了): - 将两个数字相乘将得到两倍分辨率的结果(将两个 8 位数字相乘得到一个 16 位数字) - 固定点也会错位。在此示例中,固定点之前有 3 位,之后有 5 个点。这意味着在结果数中,定点将在点之前有 6 位数字,在点之后有 10 位。
假设上述计算正确。但是当我尝试将两个负值相乘时 (-0.875 * -1.91 ~ 1.67)
110.00010 -> -1.91 (1.9375)
111.00100 -> -0.875
------------
101011.0011001000 = 011.00110(truncated to 8 bits = 3.1875)
当然,我尝试了另一个负乘法(-2.64 * -0.875 = 2.31)
101.01011 -> -2.64
111.00100 -> -0.875
----------
100110.0001001100 = 110.00010 (truncated to 8bits = -1.9375)
很明显我做错了什么,但我就是看不出我做错了什么。
PS:我还没实现呢。我想到了我要怎么做,然后我尝试用一些简单的例子手工测试它。而且我还尝试了更多的乘法。我想也许他们成功了,因为我很幸运,但显然不是,我又尝试了几次乘法,他们成功了。所以也许我在将两个负数相乘时做错了什么,也许我把它截断了?大概。
编辑: 好的,我找到了一份 Xilinx 文档,其中说明了当两个操作数为负时如何进行乘法运算,here 就是 link。根据这个文件,为了这个文件,这只能在做扩展乘法时完成。并且乘法的最后部分和必须取反,然后加 1 才能得到正确的数字。
为了乘法,我在程序员模式下使用了 windows' 计算器,这意味着为了乘以 8 位,我将数字放入计算器,然后得到结果并将其截断。如果它们适用于其他情况,则意味着 windows 计算器正在执行直接乘法(按应有的方式添加所有部分和,而不是反转最后的部分和)。所以,这意味着为了获得真正的结果,我应该从最终结果中减去第一个操作数,然后加上第一个操作数 inverted + 1
110.00010 -> -1.91 (1.9375)
111.00100 -> -0.875
------------
101011.0011001000
Which gave me the result: 000010.0111001000 = 010.01110(truncated to 8bits =2.43)
与另一个我得出的结果是 1.875。这些输出不是很好,但至少它们更接近我的预期。还有其他更简单的方法吗?
您的中间结果是错误的,因此截断没有按预期进行。此外,如果中间结果的四个 top-most 位在您的格式中相等,则只有在没有溢出的情况下才能截断。 您应该使用带符号的 data-types 进行正确的乘法运算。
即使你的第二个例子也是错误的。中间二进制结果 010101.0110000000
代表十进制数 21.375,它不是 -0.875 和 3 的乘积。所以,让我们手工做乘法:
a * b = -0.875 * 3 = -2.625
111.00100 * 011.00000
---------------------
. 00000000 // further lines containing only zeros have been omitted
+ .01100000
+ 011.00000
+ 0110.0000
+ 110100.000 // add -(2^2) * b !
= 111101.0110000000 = -2.625 (intermediate result)
= 101.01100 = -2.625 after truncation
你必须在最后的部分和中添加 b
的二进制补码,因为 a
的 top-most 位中的 '1' 表示值 -(2^2 ) = -4。此处可以进行无溢出截断,因为中间结果的 4 top-most 位相等。
现在是第三个例子
a b = -1.9375 * -0.875 = 1.6953125
110.00010 * 111.00100
---------------------
. 00000000 // further lines containing only zeros have been omitted
+ 111111.111100100 // sign-extended partial-sum
+ 111110.0100 // sign-extended partial-sum
+ 000011.100 // add -4 * b
= 000001.101100100 = 1.6953125 (intermediate result)
~ 001.10110 = 1.6875 after truncation
由于b
是一个带符号的数字,所以总是sign-extend中间结果宽度的部分和。当然,在第二个例子的计算中也这样做了,但是没有区别。