找到的实现对数的方式有三种:
- LUT
- Cordic
- 无名
LUT只能用在输入参数较少的情况中,Cordic看了下,感觉还挺复杂的,就没看了……这里主要讲第三种方式,无名是因为没有找到叫什么名字,它的基本思路是先求log2然后在用换底公式求log10。
1. 求 l o g 2 log_2 log2
观察下面的表格可以发现,只要找到了输入二进制从高位开始第一个为1的bit的位置,就能知道其log2的整数部分。至于小数部分,则可以使用查表的方式来实现,如果要精度比较高,就将表做细一点。总的来说,这种开销是很小的。
二进制 | log2 |
---|---|
00100 | 2 |
00101 | 2.3219 |
00110 | 2.5850 |
00111 | 2.8074 |
00010 | 1 |
01000 | 3 |
2. 求 l o g 10 log_{10} log10
在求得 l o g 2 log_2 log2以后,就可以通过换底公式求 l o g 10 log_{10} log10
l o g a b = l o g c b l o g c a log_ab = \frac{log_cb}{log_ca} logab=logcalogcb
不过这里要求的 10 ∗ l o g 10 10*log_{10} 10∗log10,做如下推导
10 ∗ l o g 10 b = 10 ∗ l o g 2 b l o g 2 10 = 10 ∗ l o g 2 b 3.3219 = 3.0103 ∗ l o g 2 b 10*log_{10}b =\frac{10*log_2b}{log_210} = \frac{10*log_2b}{3.3219} = 3.0103*log_2b 10∗log10b=log21010∗log2b=3.321910∗log2b=3.0103∗log2b
不管误差,在HDL实现的时候有
3 ∗ l o g 2 b = 2 ∗ l o g 2 b + l o g 2 b = l o g 2 b < < 1 + l o g 2 b 3*log_2b = 2*log_2b + log_2b = log_2b << 1 + log_2b 3∗log2b=2∗log2b+log2b=log2b<<1+log2b
所以在没有使用任何乘除法的情况下,我们就实现了 l o g 2 log_2 log2和 10 ∗ l o g 10 10*log_{10} 10∗log10
我用systemverilog实现了上述方法,代码链接如下,没分的可以留下邮箱,记得先点赞^^
https://download.csdn.net/download/qq_16923717/11570694
注意:不建议将该代码用到实际的工程中,精度不够是一个原因,更重要的是该代码中有很长的if else,时序可能不好
3. 求 l o g 10 ( a ) , 0 < a < 1 log_{10}(a), 0 < a < 1 log10(a),0<a<1
如果输入a小于1的话,上述方法就不行了,但是通过下面的公式,我们还是可以求出来。
l
o
g
a
(
x
∗
y
)
=
l
o
g
a
(
x
)
+
l
o
g
a
(
y
)
log_a(x*y) = log_a(x) + log_a(y)
loga(x∗y)=loga(x)+loga(y)
比如求
l
o
g
10
(
0.01
)
log_{10}(0.01)
log10(0.01)
l
o
g
10
(
0.01
)
=
l
o
g
10
(
10
)
−
l
o
g
10
(
1000
)
=
1
−
3
=
−
2
log_{10}(0.01) = log_{10}(10) - log_{10}(1000) = 1 - 3 =-2
log10(0.01)=log10(10)−log10(1000)=1−3=−2
这样我们只需要在输入前对
a
a
a 进行放大,然后对算出的结果减去一个固定值就好了。
.
参考链接
.
.
.
.
.
.