This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

IEC559 totalOrder


Here's an IEC559 totalOrder implementation that works with clang and GCC, for 
float, double, long double, and __float128 (at least the non-clang branch 
should work with float16, too):
https://godbolt.org/z/QzGWdB

Note that I simplified the order reversal of negative values slightly, and 
producing the bitmask for the sign is now "simpler".

As Jakub noted on IRC, 32-bit x86's long double is a problem. Since it has 
sizeof == 12 there's no *constexpr* way to cast it to an integer. 
std::bit_cast also wouldn't help. It seems there's no way around a builtin for 
this function. (Besides that, I ignored the presence of unnormal values in 
long double - you still get a totalOrder, but I'm not sure IEC559 requests a 
different behavior.)

Cheers,
  Matthias

--

#include <type_traits>
#include <limits>
#include <climits>

template <class T>
  constexpr bool totalOrder(T xx, T yy)
{
  //static_assert(std::numeric_limits<T>::is_iec559); // fails for __float128
  constexpr int Bytes = sizeof(T) == 12 ? 16 : sizeof(T);
  using II = std::conditional_t<Bytes == sizeof(signed char), signed char,
             std::conditional_t<Bytes == sizeof(signed short), signed short,
             std::conditional_t<Bytes == sizeof(signed int), signed int,
             std::conditional_t<Bytes == sizeof(signed long), signed long,
             std::conditional_t<Bytes == sizeof(signed long long), signed long 
long,
                                __int128>>>>>;
#ifdef __clang__
  constexpr int unused_bits = sizeof(T) <= 8 ? 0 : 
                            (Bytes - 8) * CHAR_BIT - 1 -
                            __builtin_ctzll((__builtin_bit_cast(II, T(-0.)) >> 
8 * CHAR_BIT));
  const II xi = __builtin_bit_cast(II, xx) << unused_bits;
  const II yi = __builtin_bit_cast(II, yy) << unused_bits;
  constexpr II signbit = __builtin_bit_cast(II, T(-0.)) << unused_bits;
  const auto x = xi < 0 ? ~(xi ^ signbit) : xi;
  const auto y = yi < 0 ? ~(yi ^ signbit) : yi;
  return x <= y;
#else
  using F [[gnu::vector_size(Bytes)]] = T;
  using I [[gnu::vector_size(Bytes)]] = II;
  constexpr int unused_bits = sizeof(T) <= 8 ? 0 : 
                            (Bytes - 8) * CHAR_BIT - 1 -
                            __builtin_ctzll((reinterpret_cast<I>(F{T(-0.)}) >> 
8 * CHAR_BIT)[0]);
  const I xi = reinterpret_cast<I>(F{xx}) << unused_bits;
  const I yi = reinterpret_cast<I>(F{yy}) << unused_bits;
  constexpr I signbit = reinterpret_cast<I>(F{T(-0.)}) << unused_bits;
  const auto x = xi[0] < 0 ? (~(xi ^ signbit))[0] : xi[0];
  const auto y = yi[0] < 0 ? (~(yi ^ signbit))[0] : yi[0];
  return x <= y;
#endif
}

template <typename T> struct Test
{
  // numeric_limits<__float128> produces bogus values
  static constexpr auto qnan = std::numeric_limits<double>::quiet_NaN();
  static constexpr auto snan = std::numeric_limits<double>::signaling_NaN();
  static constexpr auto inf  = std::numeric_limits<double>::infinity();
  
  static_assert( totalOrder<T>( 0.0,  0.0));
  static_assert( totalOrder<T>( 1.0,  1.0));
  static_assert( totalOrder<T>(-1.0, -1.0));
  static_assert( totalOrder<T>(-0.0, -0.0));
  static_assert( totalOrder<T>(qnan, qnan));
  static_assert( totalOrder<T>(snan, snan));
  static_assert( totalOrder<T>( inf,  inf));
  static_assert( totalOrder<T>(-0.0,  0.0));
  static_assert( totalOrder<T>(-0.0, +1.0));
  static_assert( totalOrder<T>(-qnan, +0.0));
  static_assert( totalOrder<T>(-qnan, -inf));
  static_assert( totalOrder<T>(-3., -2.));
  static_assert( totalOrder<T>(2., 3.));
  static_assert( totalOrder<T>(2., inf));
  static_assert( totalOrder<T>(0., qnan));
  static_assert( totalOrder<T>(0., 2.));
  static_assert( totalOrder<T>(2., qnan));
  static_assert( totalOrder<T>(inf, qnan));
  static_assert( totalOrder<T>(snan, qnan));
  static_assert(!totalOrder<T>(snan, -qnan));
  static_assert(!totalOrder<T>(0.0, -0.0));
  static_assert(!totalOrder<T>(0.0, -qnan));
  static_assert(!totalOrder<T>(0.0, -1.0));
  static_assert(!totalOrder<T>(1.0, -1.0));
  static_assert(!totalOrder<T>(qnan, -inf));
};

Test<float> a;
Test<double> b;
Test<long double> c;
Test<__float128> d;

-- 
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 std::experimental::simd              https://github.com/VcDevel/std-simd
──────────────────────────────────────────────────────────────────────────




Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]