public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
From: "g.peterhoff@t-online.de" <gcc-bugzilla@gcc.gnu.org>
To: gcc-bugs@gcc.gnu.org
Subject: [Bug libstdc++/77776] C++17 std::hypot implementation is poor
Date: Wed, 06 Mar 2024 02:47:33 +0000	[thread overview]
Message-ID: <bug-77776-4-Wv0fOXiifc@http.gcc.gnu.org/bugzilla/> (raw)
In-Reply-To: <bug-77776-4@http.gcc.gnu.org/bugzilla/>

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77776

--- Comment #19 from g.peterhoff@t-online.de ---
> So, no need to use frexp/ldexp, just comparisons of hi above against sqrt of
> (max finite / 3), in that case scale by multiplying all 3 args by some
> appropriate scale constant, and similarly otherwise if lo1 is too small by
> some large scale.

I don't really know. With frexp/ldexp you probably get the highest accuracy
(even if it is probably slower) instead of doing it manually. The problem is to
determine suitable scaling factors and to adjust the (return)values
accordingly. I have implemented both cases.

Error
* In the case (x==y && y==z), x*std::sqrt(T(3)) must not simply be returned, as
this can lead to an overflow (inf).

Generally
* Instead of using fmin/fmax to determine the values hi,lo1,lo0, it is better
to sort x,y,z. This is faster and clearer and no additional variables need to
be introduced.
* It also makes sense to consider the case (x==0 && y==0 && z==0).

Optimizations
* You were probably wondering why I wrote "if (std::isinf(x) | std::isinf(y) |
std::isinf(z))", for example. This is intentional. The problem is that gcc
almost always produces branch code for logical operations, so *a lot* of
conditional jumps. By using arithmetic operations, so instead of || && just |
&, I can get it to generate only actually necessary conditional jumps or
cmoves. branchfree code is always better.


template <typename T>
constexpr T     hypot3_exp(T x, T y, T z) noexcept
{
        using limits = std::numeric_limits<T>;

        constexpr T
                zero = 0;

        x = std::abs(x);
        y = std::abs(y);
        z = std::abs(z);

        if (std::isinf(x) | std::isinf(y) | std::isinf(z))  [[unlikely]]
                return limits::infinity();
        if (std::isnan(x) | std::isnan(y) | std::isnan(z))      [[unlikely]]
                return limits::quiet_NaN();
        if ((x==zero) & (y==zero) & (z==zero))  [[unlikely]]
                return zero;
        if ((y==zero) & (z==zero))      [[unlikely]]
                return x;
        if ((x==zero) & (z==zero))      [[unlikely]]
                return y;
        if ((x==zero) & (y==zero))      [[unlikely]]
                return z;

        auto sort = [](T& a, T& b, T& c)        constexpr noexcept -> void
        {
                if (a > b) std::swap(a, b);
                if (b > c) std::swap(b, c);
                if (a > b) std::swap(a, b);
        };

        sort(x, y, z);  //      x <= y <= z

        int
                exp = 0;

        z = std::frexp(z, &exp);
        y = std::ldexp(y, -exp);
        x = std::ldexp(x, -exp);

        T
                sum = x*x + y*y;

        sum += z*z;
        return std::ldexp(std::sqrt(sum), exp);
}

template <typename T>
constexpr T     hypot3_scale(T x, T y, T z) noexcept
{
        using limits = std::numeric_limits<T>;

        auto prev_power2 = [](const T value)    constexpr noexcept -> T
        {
                return std::exp2(std::floor(std::log2(value)));
        };

        constexpr T
                sqrtmax         = std::sqrt(limits::max()),
                scale_up        = prev_power2(sqrtmax),
                scale_down      = T(1) / scale_up,
                zero            = 0;

        x = std::abs(x);
        y = std::abs(y);
        z = std::abs(z);

        if (std::isinf(x) | std::isinf(y) | std::isinf(z))  [[unlikely]]
                return limits::infinity();
        if (std::isnan(x) | std::isnan(y) | std::isnan(z))      [[unlikely]]
                return limits::quiet_NaN();
        if ((x==zero) & (y==zero) & (z==zero))  [[unlikely]]
                return zero;
        if ((y==zero) & (z==zero))      [[unlikely]]
                return x;
        if ((x==zero) & (z==zero))      [[unlikely]]
                return y;
        if ((x==zero) & (y==zero))      [[unlikely]]
                return z;

        auto sort = [](T& a, T& b, T& c)        constexpr noexcept -> void
        {
                if (a > b) std::swap(a, b);
                if (b > c) std::swap(b, c);
                if (a > b) std::swap(a, b);
        };

        sort(x, y, z);  //      x <= y <= z

        const T
                scale = (z > sqrtmax) ? scale_down : (z < 1) ? scale_up : 1;

        x *= scale;
        y *= scale;
        z *= scale;

        T
                sum = x*x + y*y;

        sum += z*z;
        return std::sqrt(sum) / scale;
}


regards
Gero

  parent reply	other threads:[~2024-03-06  2:47 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <bug-77776-4@http.gcc.gnu.org/bugzilla/>
2021-05-04 12:31 ` rguenth at gcc dot gnu.org
2024-02-29 18:23 ` g.peterhoff@t-online.de
2024-03-01 12:27 ` redi at gcc dot gnu.org
2024-03-02 15:52 ` g.peterhoff@t-online.de
2024-03-04  3:49 ` de34 at live dot cn
2024-03-04 10:45 ` mkretz at gcc dot gnu.org
2024-03-04 11:12 ` jakub at gcc dot gnu.org
2024-03-04 17:14 ` mkretz at gcc dot gnu.org
2024-03-04 20:21 ` jakub at gcc dot gnu.org
2024-03-06  2:47 ` g.peterhoff@t-online.de [this message]
2024-03-06  9:43 ` mkretz at gcc dot gnu.org
2024-03-06 12:35 ` redi at gcc dot gnu.org
2024-03-12 11:31 ` mkretz at gcc dot gnu.org
2024-03-19  0:09 ` g.peterhoff@t-online.de
2024-03-25 10:06 ` mkretz at gcc dot gnu.org
2024-04-05  1:08 ` g.peterhoff@t-online.de
2024-04-05  1:23 ` g.peterhoff@t-online.de
2024-04-10 16:41 ` g.peterhoff@t-online.de
2024-04-10 16:48 ` jakub at gcc dot gnu.org
2024-04-10 18:08 ` g.peterhoff@t-online.de

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=bug-77776-4-Wv0fOXiifc@http.gcc.gnu.org/bugzilla/ \
    --to=gcc-bugzilla@gcc.gnu.org \
    --cc=gcc-bugs@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).