Skip to content
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

constexpr frexp #686

Merged
merged 6 commits into from
Sep 6, 2021
Merged

constexpr frexp #686

merged 6 commits into from
Sep 6, 2021

Conversation

mborland
Copy link
Member

@mborland mborland commented Sep 3, 2021

@ckormanyos This was borrowed heavily from your wide integer implementation with some added handling described in the standard (e.g. NaN). Do you have a sample constexpr test you use? I am not sure how to get around exp having external visibility in a constexpr context. I could change to something like frexp(T arg, void), but then the behavior would be different from the standard.

@ckormanyos
Copy link
Member

ckormanyos commented Sep 3, 2021

Do you have a sample constexpr test you use?

Matt, thanks for pushing ahead on this function and the constexpr drive as an entire body of work.

My tests only used my implementation within the context of larger constexpr algorithms. So I do not, per se, have any explicit tests of constexpr frexp.

I am, however, convinced that the addess operator of the signed integer exponent argument (i.e., as in double frexp(double, int*)) rules out C++11 constexpr-ness entirely and requires constexpr from C++14 and higher standards. So this means that the word constexpr might not be allowed on that function when compiling with C++11. I was only able to achieve constexpr-ness on frexp-like and ldexp-like functions for C++14 or higher.

I will need to look at your implementation @mborland more closely. Once a differentiation is made between C++11/14-and-beyond constexpr-ness, my first thought is to start with some really simple examples such as the one below.

  // N[125/32, 30]
  // 3.90625000000000000000000000000

  int n;

  // 0.976562500000000000000000000000 * 2^2
  const double x = std::frexp(3.90625, &n);

Unlike the traditional code above, however, you could use the constexpr version and as a first step check if x can be created and queried within a constexpr context.

@mborland
Copy link
Member Author

mborland commented Sep 3, 2021

@ckormanyos This code is all written using C++17 so the definition of constexpr should not be an issue. The following trivial test yields error: modification of ‘n’ is not a constant expression

template <typename T>
void test()
{
    int n;
    constexpr T x = boost::math::ccmath::frexp(T(3.90625), &n);
}

I am not entirely sure this error can be avoided.

@jzmaddock
Copy link
Collaborator

I am not entirely sure this error can be avoided.

You can't use the function in that manner: it would have to be within the body of a larger constexpr function.

@mborland
Copy link
Member Author

mborland commented Sep 6, 2021

Pending review this is clean on CI. Next in the queue is ldexp by request. Adding @NAThompson.

template <typename Real>
inline constexpr Real frexp(Real arg, int* exp);

template <typename Z>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trivial quibble, but I when I first read this I didn't grok that this was limited to integer arguments, maybe use <typename Integer> and perhaps a comment that this is for integer arguments?

Other than that, I'm happy for this to be merged, and you'll be pleased to know that ldexp should be noticeably easier! :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jzmaddock : That was my (perhaps poor) convention; mimicking ℤ.

Is it possible to do a concept without breaking C++17 compatibility?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really, although the enable_if does much the same thing. It's debatable whether the enable_if condition should be part of the declared interface - it clutters up the docs, but does at least make it clear what the requirements on Z are.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doc suggestions have been incorporated in the latest commit.

As for concepts, you can avoid breaking C++17 compatibility using this macro (taken from good concepts):

#ifdef GOOD_COMPILER 
#define REQUIRES requires 
#elseif 
#define REQUIRES // 
#endif 

@NAThompson NAThompson merged commit 0bbea8d into boostorg:develop Sep 6, 2021
@mborland mborland deleted the frexp branch September 6, 2021 18:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants