我有一个16位数,我想要除以100.假设它是50000.目标是获得500.但是,我试图避免推断我的FPGA上的分频器,因为它们打破了时序要求。结果不一定准确;近似值可以。
我尝试过硬件乘法0.01但不支持实数。我现在正在看流水线分频器,但我希望它不会出现这种情况。
答案 0 :(得分:12)
概念上:乘以655(= 65536/100)然后向右移16位。当然,在硬件方面,右移是免费的。
如果你需要它更快,你可以将除法线作为两个幂(移位)的除法之和。例如,
1/100 ~= 1/128 = 0.0078125
1/100 ~= 1/128 + 1/256 = 0.01171875
1/100 ~= 1/128 + 1/512 = 0.009765625
1/100 ~= 1/128 + 1/512 + 1/2048 = 0.01025390625
1/100 ~= 1/128 + 1/512 + 1/4096 = 0.010009765625
etc.
在C代码中,上面的最后一个例子是:
uint16_t divideBy100 (uint16_t input)
{
return (input >> 7) + (input >> 9) + (input >> 12);
}
答案 1 :(得分:4)
假设
然后您可以通过实现16x16无符号乘法器来获得精确值,其中一个输入为0xA3D7,另一个输入为您的16位数。将0x8000添加到32位乘积,结果为高10位。
在C代码中,算法看起来像这样
uint16_t divideBy100( uint16_t input )
{
uint32_t temp;
temp = input;
temp *= 0xA3D7; // compute the 32-bit product of two 16-bit unsigned numbers
temp += 0x8000; // adjust the 32-bit product since 0xA3D7 is actually a little low
temp >>= 22; // the upper 10-bits are the answer
return( (uint16_t)temp );
}
答案 2 :(得分:1)
通常,您可以乘以逆和移位。编译器一直这样做,即使对于软件也是如此
这是一个为您执行此操作的页面:http://www.hackersdelight.org/magic.htm
在你的情况下,似乎乘以0x431BDE83,然后右移17。
这是一个解释:Computing the Multiplicative Inverse for Optimizing Integer Division
答案 3 :(得分:0)
乘以倒数通常是一种很好的方法,正如您所指出的那样,虽然不支持实数。您需要使用固定点而不是浮点数。
Verilog没有定点的定义,但它只是使用字长,你可以决定有多少位是整数,有多少是小数。
二进制的 0.01(0.0098876953125)为0_0000001010001
。这个字长越大,精度越高。
// 1Int, 13Frac
wire ONE_HUNDREDTH = 14'b0_0000001010001 ;
input a [15:0]; //Integer (no fractional bits)
output result [15+14:0]; //13 fractional bits inherited form ONE_HUNDREDTH
output result_int [15:0]; //Integer result
always @* begin
result = ONE_HUNDREDTH * a;
result_int = result >>> 13;
end
使用ruby gem fixed_point进行实数到二进制转换。
ruby irb会话(通过gem install fixed_point
安装了fixed_point):
require 'fixed_point'
#Unsigned, 1 Integer bit, 13 fractional bits
format = FixedPoint::Format.new(0, 1, 13)
fix_num = FixedPoint::Number.new(0.01, format )
=> 0.0098876953125
fix_num.to_b
=> "0.0000001010001"