Skip to content

<cmath> does not publish std::fpclassify() #40

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
ckormanyos opened this issue May 12, 2025 · 4 comments · May be fixed by #41
Open

<cmath> does not publish std::fpclassify() #40

ckormanyos opened this issue May 12, 2025 · 4 comments · May be fixed by #41

Comments

@ckormanyos
Copy link
Collaborator

ckormanyos commented May 12, 2025

I was doing some standard mathematical calculations and I found empirically that

  • <cmath> does not publish std::fpclassify.
  • There are also no definitions for FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL and FP_ZERO.

There is an implementation of fpclassify in std present in the file, but it is hidden within some compiler switches that I don't understand. It is rather straightforward to implement, if that is the answer here.

If I'm not mistaken, I think we need in namespace std:

int fpclassify( float num );
int fpclassify( double num );
int fpclassify( long double num );

And I think we also need the constants FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL and FP_ZERO.

@ckormanyos
Copy link
Collaborator Author

ckormanyos commented May 12, 2025

The compiler has a __builtin_ for fpclassify but it needs the right definitions of the constants then too.

A very simple implementation (if that's needed) goes something like this:

#if !defined(FP_NAN)
#define FP_NAN         0
#endif
#if !defined(FP_INFINITE)
#define FP_INFINITE    1
#endif
#if !defined(FP_ZERO)
#define FP_ZERO        2
#endif
#if !defined(FP_SUBNORMAL)
#define FP_SUBNORMAL   3
#endif
#if !defined(FP_NORMAL)
#define FP_NORMAL      4
#endif

namespace std {

// a bunch of other stuff, also isnan, fabs, and more...

namespace cmath_detail {

template<typename FloatType>
inline int fpclassify_impl(FloatType x)
{
  #if defined(__GNUC__)
  #pragma GCC diagnostic push
  #pragma GCC diagnostic ignored "-Wfloat-equal"
  #endif

  using float_type = FloatType;

  if((::std::isnan)(x))
  {
    return FP_NAN;
  }
  else if((::std::isinf)(x))
  {
    return FP_INFINITE;
  }
  else if((::std::fabs)(x) == static_cast<float_type>(0.0L))
  {
    return FP_ZERO;
  }
  else
  {
    const bool
      is_subnormal
      {
        (
             ((::std::fabs)(x) > static_cast<float_type>(0.0L))
          && ((::std::fabs)(x) < (std::numeric_limits<float_type>::min)())
        )
      };

    return (is_subnormal ? FP_SUBNORMAL : FP_NORMAL);
  }

  #if defined(__GNUC__)
  #pragma GCC diagnostic pop
  #endif
}

} // namespace cmath_detail

inline int fpclassify(float x)       { return ::std::cmath_detail::fpclassify_impl(x); }
inline int fpclassify(double x)      { return ::std::cmath_detail::fpclassify_impl(x); }
inline int fpclassify(long double x) { return ::std::cmath_detail::fpclassify_impl(x); }

} // namespace std

@jwakely
Copy link

jwakely commented May 12, 2025

I don't think you need to define it. If you have the constants, you can use the compiler built-in. I think that is always expanded by the compiler, so doesn't require anything from avr-libc to work (just the constants).

https://gcc.gnu.org/onlinedocs/gcc/Floating-Point-Format-Builtins.html#index-_005f_005fbuiltin_005ffpclassify

There is an implementation of fpclassify in std present in the file, but it is hidden within some compiler switches that I don't understand.

#if _GLIBCXX_USE_C99_MATH

In upstream libstdc++ this gets set when configuring GCC, based on this autoconf test:

        [#include <math.h>
         volatile double d1, d2;
         volatile int i;],
        [i = fpclassify(d1);
         i = isfinite(d1);
         i = isinf(d1);
         i = isnan(d1);
         i = isnormal(d1);
         i = signbit(d1);
         i = isgreater(d1, d2);
         i = isgreaterequal(d1, d2);
         i = isless(d1, d2);
         i = islessequal(d1, d2);
         i = islessgreater(d1, d2);
         i = islessgreater(d1, d2);
         i = isunordered(d1, d2);
        ],

We test that separately for C++98 mode and C++11 mode and define separate _GLIBCXX98_USE_C99_MATH and _GLIBCXX11_USE_C99_MATH macros, and then at compile-time _GLIBCXX_USE_C99_MATH uses the value of one of the 98 or 11 macros, based on the value of __cplusplus (i.e. the -std option used during compilation).

So libstdc++ will not define any of std::fpclassify, std::isfinite etc. unless all of those are present in libc. I think we should improve that, because as stated above, std::fpclassify doesn't need anything from libc except the constants.

#ifndef __CORRECT_ISO_CPP11_MATH_H_PROTO_FP

This is defined if libc already provides std::fpclassify defined appropriately for C++11, which is true for Solaris, but not most other C libraries, IIRC.

#if !_GLIBCXX_USE_C99_FP_MACROS_DYNAMIC

This is only relevant for OpenBSD and vxworks.

@jwakely
Copy link

jwakely commented May 12, 2025

So libstdc++ will not define any of std::fpclassify, std::isfinite etc. unless all of those are present in libc. I think we should improve that, because as stated above, std::fpclassify doesn't need anything from libc except the constants.

I opened https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120232 upstream. I don't plan to work on it any time soon though (and it wouldn't help avr-libstdcpp unless you sync with upstream).

@ckormanyos ckormanyos linked a pull request May 12, 2025 that will close this issue
@ckormanyos
Copy link
Collaborator Author

ckormanyos commented May 12, 2025

See also #41.

Thanks @jwakely I actually think a naive exclusion of C++11 functions was the culprit in this problem. I did a trivial fix. I could not believe that this is upstream. But you might want to check anyway.

Cc: @salkinium and @chris-durand

@salkinium salkinium linked a pull request May 14, 2025 that will close this issue
jwakely added a commit to jwakely/avr-libstdcpp that referenced this issue May 14, 2025
This partially reverts 5a4c53d and adds
the constants needed for it to work.

Fixes: modm-io#40
jwakely added a commit to jwakely/avr-libstdcpp that referenced this issue May 14, 2025
This partially reverts 5a4c53d and adds
the constants needed for it to work.

Fixes: modm-io#40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
2 participants