我怎样才能在verilog中做更准确的计算ln(1+x)
How can I do more accurate calculation ln(1+x) in verliog
我想在 verilog A 中计算 ln(1+x)。
我知道密码是
y = ln(1+x);
但是如果x值变得非常小(ex x=3.52e-18),y值变为零。
在 MATLAB 中,我可以这样计算
y=log1p(x);
我想像那个代码那样计算。
怎样计算更准确?
添加不属于 IEEE 标准的数学函数的唯一方法是使用 C 接口。
在 SystemVerilog 中,您可以添加此行而不必编写任何 C 代码
import "DPI-C" function real log1p(real arg);
但是在 Verilog 中,您必须从必须用 C 语言编写的代码中调用调用 log1p
的 VPI 包装函数。
当然,您始终可以编写 log1p 的多项式或有理数或 table-driven 近似值,或者找到其他人已经实现它的库。
但是如果您已经有一个计算 log() 的函数,那么在紧要关头有一个计算 log(1 + ) 的小技巧!
- 如果
1 + x = 1
——也就是说,如果 fl(1 + ) = 1——那么只是 return .
- 否则,计算
x*log(1 + x)/((1 + x) − 1)
(分母中的括号完全相同)。
为什么这样做?
对于||的简单情况> 1/2,这是有效的,因为 fl(1 + ) 在最坏的情况下只会损失一位精度,因此商 /(fl(1 + ) − 1) 基本上抵消(或完全抵消,如果 ≤ −1/2或 ≥ 1) 基本上只留下 log(fl(1 + )) ≈ log(1 + ).
对于 ulp(1)/2 < || 的有趣案例≤ 1/2,这是可行的,因为我们对 ⋅() 的近似因子,其中 () := log(1 + )/ 在零附近条件非常好,因此即使我们实际评估 ( fl(1 + ) - 1)。
因此,虽然 log(1 + x)
单独放大了 1 + x
中的舍入误差,
x*log(1 + x)/((1 + x) - 1)
在几个 ulps 范围内给出了 log(1 + ) 的一个很好的近似值(前提是你的 log
函数有小误差)。
详情:
- fl(1 + ) − 1的绝对误差至多为ulp(1)/2。
- () ≥ 1/2 所有 || ≤ 1/2.
- |′()|所有 || ≤ 2 ≤ 1/2.
因此根据中值定理,在 和 fl(1 + ) − 1 之间存在一些使得
|() − (fl(1 + ) − 1)| = |′()⋅( − [fl(1 + ) − 1])| ≤ 2⋅|1 + − fl(1 + )| ≤ 2⋅ulp(1)/2 = ulp(1),
因此(fl(1 + ) − 1)与()的相对误差为|() − (fl(1 + ) − 1)|/|()| ≤ 2⋅ulp(1)。
而 (fl(1 + ) − 1) 是 log(1 + x)/((1 + x) - 1)
的近似值。
(对于正输入,|′()| ≤ 1/2 因此界限提高到 ulp(1)/2。)
1 + x = 1
的边缘情况当且仅当 || 发生≤ ulp(1)/2。
当这个成立时,由于 log(1 + ) = + O(²) 级数截断的误差(具体来说,最多 ||/2 ≤ ulp(1)/4,如果您查看级数的确切项)是不比舍入误差差。
这种情况必须特殊处理,以避免被 (1 + x) - 1
.
给出的零除
(此技术作为定理 4 出现在戈德堡关于 What Every Computer Scientist Should Know about Floating-Point Arithmetic 的臭名昭著的笔记中。)
我想在 verilog A 中计算 ln(1+x)。 我知道密码是
y = ln(1+x);
但是如果x值变得非常小(ex x=3.52e-18),y值变为零。 在 MATLAB 中,我可以这样计算
y=log1p(x);
我想像那个代码那样计算。 怎样计算更准确?
添加不属于 IEEE 标准的数学函数的唯一方法是使用 C 接口。
在 SystemVerilog 中,您可以添加此行而不必编写任何 C 代码
import "DPI-C" function real log1p(real arg);
但是在 Verilog 中,您必须从必须用 C 语言编写的代码中调用调用 log1p
的 VPI 包装函数。
当然,您始终可以编写 log1p 的多项式或有理数或 table-driven 近似值,或者找到其他人已经实现它的库。
但是如果您已经有一个计算 log() 的函数,那么在紧要关头有一个计算 log(1 + ) 的小技巧!
- 如果
1 + x = 1
——也就是说,如果 fl(1 + ) = 1——那么只是 return . - 否则,计算
x*log(1 + x)/((1 + x) − 1)
(分母中的括号完全相同)。
为什么这样做?
对于||的简单情况> 1/2,这是有效的,因为 fl(1 + ) 在最坏的情况下只会损失一位精度,因此商 /(fl(1 + ) − 1) 基本上抵消(或完全抵消,如果 ≤ −1/2或 ≥ 1) 基本上只留下 log(fl(1 + )) ≈ log(1 + ).
对于 ulp(1)/2 < || 的有趣案例≤ 1/2,这是可行的,因为我们对 ⋅() 的近似因子,其中 () := log(1 + )/ 在零附近条件非常好,因此即使我们实际评估 ( fl(1 + ) - 1)。 因此,虽然
log(1 + x)
单独放大了1 + x
中的舍入误差,x*log(1 + x)/((1 + x) - 1)
在几个 ulps 范围内给出了 log(1 + ) 的一个很好的近似值(前提是你的log
函数有小误差)。详情:
- fl(1 + ) − 1的绝对误差至多为ulp(1)/2。
- () ≥ 1/2 所有 || ≤ 1/2.
- |′()|所有 || ≤ 2 ≤ 1/2.
因此根据中值定理,在 和 fl(1 + ) − 1 之间存在一些使得
|() − (fl(1 + ) − 1)| = |′()⋅( − [fl(1 + ) − 1])| ≤ 2⋅|1 + − fl(1 + )| ≤ 2⋅ulp(1)/2 = ulp(1),
因此(fl(1 + ) − 1)与()的相对误差为|() − (fl(1 + ) − 1)|/|()| ≤ 2⋅ulp(1)。 而 (fl(1 + ) − 1) 是
log(1 + x)/((1 + x) - 1)
的近似值。(对于正输入,|′()| ≤ 1/2 因此界限提高到 ulp(1)/2。)
给出的零除1 + x = 1
的边缘情况当且仅当 || 发生≤ ulp(1)/2。 当这个成立时,由于 log(1 + ) = + O(²) 级数截断的误差(具体来说,最多 ||/2 ≤ ulp(1)/4,如果您查看级数的确切项)是不比舍入误差差。 这种情况必须特殊处理,以避免被(1 + x) - 1
.
(此技术作为定理 4 出现在戈德堡关于 What Every Computer Scientist Should Know about Floating-Point Arithmetic 的臭名昭著的笔记中。)