Home > Software design >  Need to calculate the complex log of a double precision float
Need to calculate the complex log of a double precision float

Time:02-02

I tried computing the real part of clog(a i*b) using the following approach

Consider 'x' to be the complex number. x = a i*b Let z be the complex log of x.

real(x) = 0.5 * log(a^2 b^2)

This approach gives a huge error in terms of ULP for values between 0.5 and 1.0 especially.

I tried other approaches to avoid squaring of both the real and imaginary parts such as

Let t = b / a; real(x) = log(a) 0.5 * log1p(t*t)

The error continued to persist with this approach as well. I understand that the error is likely from the squaring of a and b and hence I tried using fma() operations to get the error due to the squaring of 'a' and 'b'

Let a2 = a * a b2 = b * b

err_a2 = fma(a,a, -a2)

err_b2 = fma(b,b,-b2)

I then tried 0.5 * log(((err_a1 err_b2) a2) b2) to get the real value of the complex log of x.

But the result is still inaccurate.

How can I compute log(sqrt(a^2 b^2)) accurately (error within 2 ULP). I think I need to compute the square root of a^2 b^2 in higher precision at a higher precision but I am not sure how to proceed from here.

CodePudding user response:

sqrt(a^2 b^2) is just std::hypot(a,b). With a bit of luck, that's already precise.

CodePudding user response:

... calculate the complex log of a double ...

Code could use double real_part = (double) clog(x).


To calculate the real part of a complex log of a double without using clog(x) near |x| == 1.0, consider using log1p()*1 to form a better precision result.

The core issue is |x| - 1.0 can suffer severe loss of precision and this is the first step in determining log().

0.5 * log(a^2 b^2) is mathematically like 0.5 * logp1(a^2 b^2 - 1). When |x| is near 1.0 and |a| > |b|, use 0.5 * logp1((a-1)*(a 1) b^2). This subtracts the 1.0 from |a| exactly and thus retains precision in (a-1)*(a 1) b^2.

#include <complex.h>
#include <math.h>
#include <stdio.h>

#define root2 1.4142135623730950488016887242097

double clog_real(double a, double b) {
  double real_x;
  double h = hypot(a, b);
  // |x| near 1.0?
  if (h >= root2 / 2 && h < root2) {
    // Subtract 1 from the larger part
    if (fabs(a) > fabs(b)) {
      real_x = 0.5 * log1p((a - 1) * (a   1)   b * b);
    } else {
      real_x = 0.5 * log1p((b - 1) * (b   1)   a * a);
      // or (here and like-wise above)
      real_x = 0.5 * log1p(fma(a, a, (b - 1) * (b   1)));
    }
  } else {
    real_x = log(h);
  }
  return real_x;
}

int main() {
  double a = 0x1.fffffe0000010p-12 * 2;
  double b = 0x1.fffffc0000040p-1;
  printf("%g %g\n", a, b);
  complex double c = a   csqrt(-1) * b;
  printf("%g\n", (double) clog(c));
  printf("%g\n", clog_real(a, b));
}

Output

0.000976562 1
3.57628e-07
3.57628e-07

Re: "I tried using fma() ..." --> Some fma() are low quality.


*1 The log1p functions compute the base-e (natural) logarithm of 1 plus the argument.

  •  Tags:  
  • Related