如何在verliog中进行更准确的计算ln(1+x)

时间:2021-02-01 09:01:15

标签: verilog

我想在 verilog A 中计算 ln(1+x)。 我知道代码是

<块引用>

y = ln(1+x);

但如果 x 值变得非常小(例如 x=3.52e-18),则 y 值变为零。 在 MATLAB 中,我可以像这样计算

<块引用>

y=log1p(x);

我想像那个代码一样计算。 如何计算更准确?

2 个答案:

答案 0 :(得分:2)

添加不属于 IEEE 标准的数学函数的唯一方法是使用 C 接口。

在 SystemVerilog 中,您可以添加这一行,而不必编写任何 C 代码

import "DPI-C" function real log1p(real arg);

但在 Verilog 中,您必须调用一个 VPI 包装函数,该函数从您必须用 C 编写的代码中调用 log1p

答案 1 :(得分:1)

当然,您始终可以编写多项式或有理数或 table-driven 对 log1p 的近似,或者找到其他人已经实现它的库。

但是如果您已经有计算 log(?) 的函数,那么在紧要关头给定 ? 时,有一个计算 log(1 + ?) 的廉价技巧!

  1. 如果1 + x = 1——也就是说,如果fl(1 + ?) = 1——那么就返回?。
  2. 否则,计算 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 函数有很小的误差)。

    详情:

    1. fl(1 + ?) − 1 从 ? 的绝对误差至多为 ulp(1)/2。
    2. ?(?) ≥ 1/2 对所有 |?| ≤ 1/2。
    3. |?′(?)| ≤ 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,如果你看一下series) 并不比舍入误差差。 这种情况必须特别处理,以避免除以由 (1 + x) - 1 给出的零。

(这种技术在戈德堡关于 What Every Computer Scientist Should Know about Floating-Point Arithmetic 的臭名昭著的笔记中作为定理 4 出现。)