Преобразование из декартовых в сферические координаты и неточности с плавающей точкой - PullRequest
0 голосов
/ 15 февраля 2020

Мне нужно преобразовать декартову в сферические координаты, но моя реализация страдает от неточности с плавающей точкой. Вот что я делаю до сих пор:

template<typename RealType>
Point3<RealType> cartesian_to_spherical(Point3<RealType> const& p)
{
    auto const &x = p.x, &y = p.y, &z = p.z;
//  (x, y, z) = (r sin θ cos ϕ, r sin θ sin ϕ, r cos θ)

    auto const r_squared = x * x + y * y + z * z;
    if (r_squared > 0)
    {
        auto const r = std::sqrt(r_squared);
        auto const theta = std::acos(z / r);

        if (0 < theta && theta < pi<RealType>)
        {
            RealType phi;
            if (x > 0)
            {
                phi = std::atan(y / x);
                if (y < 0)
                    phi += 2 * pi<RealType>;
            }
            else if (x < 0)
                phi = pi<RealType> + std::atan(y / x);
            else // x == 0
                phi = y > 0 ? phi = pi<RealType> / 2 : 3 * pi<RealType> / 2;

            return { r, theta, phi };
        }
        else
            throw std::domain_error("theta = 0 or theta = pi");
    }
    else
        throw std::domain_error("r = 0");
}

Например, если cartesian_to_spherical вызывается с RealType = float и p = {.000157882227f, .000284417125f, 1 }, тогда r_squared равно 1.00000012 и его квадрат root r вычисляется до 1 (что явно неверно). Как следствие, theta вычисляется как 0, тогда как правильное значение будет 0.000325299694....

Можем ли мы улучшить код так, чтобы эти вычисления были более точными?

( Возможно, вы захотите принять к сведению преобразование, описанное здесь с использованием std::atan2. Однако, если я что-то не пропустил, это даст неправильный результат, если std::sin(theta) равно 0 (т.е. theta равно 0 или pi) и также вычисляет theta до 0 в примере.)

...