https://issues.dlang.org/show_bug.cgi?id=23750

          Issue ID: 23750
           Summary: log1p for floats/doubles not actually providing extra
                    accuracy
           Product: D
           Version: D2
          Hardware: x86
                OS: Windows
            Status: NEW
          Severity: normal
          Priority: P1
         Component: phobos
          Assignee: nob...@puremagic.com
          Reporter: john.michael.h...@gmail.com

As discussed in issue 23677 [1], overloads for log1p were recently added for
floats and doubles. However, these versions don't seem to actually be providing
the additional accuracy that the CEPHES implementation that it is based on
provides. These functions forward to a logImpl function that seems to have some
the CEPHES code, but doesn't actually produce the more accurate result that
CEPHES provides.

I adapted the code below based on a CEPHES mirror [2] for doubles. This
implementation of log1p provides a much more accurate result than the
implementation in phobos (not shown, but comparing to the result before these
overloads were added). Moreover, it doesn't experience the same failure when
the value of x gets smaller. 

I compiled with DMD 2.102. 

```d
enum double SQRTH = 0.70710678118654752440;
enum double SQRT2 = 1.41421356237309504880;

double log1p(double x) {
    import std.math.exponential: log;
    import std.math.algebraic: poly;
    static immutable double[] LP = [
     2.0039553499201281259648E1,
     5.7112963590585538103336E1,
     6.0949667980987787057556E1,
     2.9911919328553073277375E1,
     6.5787325942061044846969E0,
     4.9854102823193375972212E-1,
     4.5270000862445199635215E-5,
    ];
    static immutable double[] LQ = [
     6.0118660497603843919306E1,
     2.1642788614495947685003E2,
     3.0909872225312059774938E2,
     2.2176239823732856465394E2,
     8.3047565967967209469434E1,
     1.5062909083469192043167E1,
     1.0000000000000000000000E0,
    ];
    double z = 1.0 + x;
    if ((z < SQRTH) || (z > SQRT2))
        return log(z);
    z = x * x;
    z = -0.5 * z + x * (z * poly(x, LP) / poly(x, LQ));
    return x + z;
}

void main() {
    import std.stdio;
    static import std.math;
    double x = 1e-15;
    double y = std.math.log(1 + x);
    double z1 = log1p(x);
    double z2 = std.math.log1p(x);
    double z3 = log1p(x / 10);
    double z4 = std.math.log1p(x / 10);
    writefln("the value of x is %.20e", x);//prints around 1.0e-15
    writefln("the value of y is %.20e", y); //prints around 1.11e-15
    writefln("the value of z1 is %.20e", z1); //prints around 9.99e-16
    writefln("the value of z2 is %.20e", z2); //prints around 1.11e-15
    writefln("the value of z3 is %.20e", z3); //prints around 1.0e-15
    writefln("the value of z4 is %.20e", z4); //prints 0
}
```

[1] https://issues.dlang.org/show_bug.cgi?id=23677
[2] https://github.com/jeremybarnes/cephes/blob/master/cprob/unity.c

--

Reply via email to