How can I do more accurate calculation ln(1+x) in verliog

Viewed 105

I want to calculate ln(1+x)in verilog A. I know that the code is

y = ln(1+x);

But if the x value becomes very small(ex x=3.52e-18), the y value becomes zero. In MATLAB, I can calculate like

y=log1p(x);

and I want to calculate like that code. How can I calculate more accurately?

2 Answers

The only way to add math functions that are not part of the IEEE standard are by using the C interface.

In SystemVerilog, you can add this line and not have to write any C code

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

But in Verilog, you would have to call a VPI wrapper function that called log1p from code that you would have to write in C.

You can always write a polynomial or rational or table-driven approximation to log1p, or find a library where someone else already implemented it, of course.

But there's a cheap trick to compute log(1 + ) given in a pinch if you already have a function to compute log()!

  1. If 1 + x = 1—that is, if fl(1 + ) = 1—then just return .
  2. Otherwise, evaluate x*log(1 + x)/((1 + x) − 1) (with exactly that parenthesization in the denominator).

Why does this work?

  • For the easy case of || > 1/2, this works because fl(1 + ) only loses at worst a single bit of precision from , so the quotient /(fl(1 + ) − 1) essentially cancels (or exactly cancels, if ≤ −1/2 or ≥ 1) leaving essentially only log(fl(1 + )) ≈ log(1 + ).

  • For the interesting case of ulp(1)/2 < || ≤ 1/2, this works because our approximation factors into ⋅() where () := log(1 + )/ is very well-conditioned near zero so that we still get a good approximation to () even if we actually evaluate (fl(1 + ) − 1). So while log(1 + x) alone magnifies the rounding error in 1 + x, x*log(1 + x)/((1 + x) - 1) gives a good approximation to log(1 + ), within a handful of ulps (provided your log function has small error).

    Details:

    1. The absolute error of fl(1 + ) − 1 from is at most ulp(1)/2.
    2. () ≥ 1/2 for all || ≤ 1/2.
    3. |′()| ≤ 2 for all || ≤ 1/2.

    Hence by the mean value theorem there is some between and fl(1 + ) − 1 such that

    |() − (fl(1 + ) − 1)| = |′()⋅( − [fl(1 + ) − 1])| ≤ 2⋅|1 + − fl(1 + )| ≤ 2⋅ulp(1)/2 = ulp(1),

    so that the relative error of (fl(1 + ) − 1) from () is |() − (fl(1 + ) − 1)|/|()| ≤ 2⋅ulp(1). And (fl(1 + ) − 1) is what log(1 + x)/((1 + x) - 1) approximates.

    (For positive inputs, |′()| ≤ 1/2 so the bound improves to ulp(1)/2.)

  • The edge case where 1 + x = 1 happens if and only if || ≤ ulp(1)/2. When this holds, since log(1 + ) = + O(²) the error of series truncation (specifically, at most ||/2 ≤ ulp(1)/4, if you look at the exact terms of the series) is no worse than a rounding error. This case must be treated specially in order to avoid dividing by zero given by (1 + x) - 1.

(This technique appears as Theorem 4 in Goldberg's infamous note on What Every Computer Scientist Should Know about Floating-Point Arithmetic.)

Related