C++ Logo

std-proposals

Advanced search

Re: [std-proposals] std::next_higher, std::next_lower

From: Charles R Hogg <charles.r.hogg_at_[hidden]>
Date: Sat, 7 Dec 2024 12:28:54 -0500
On Sat, Dec 7, 2024 at 11:38 AM Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
wrote:

> On Sat, Dec 7, 2024 at 10:24 AM Charles R Hogg via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>> `std::nextafter` and friends provide the next representable value after
>> the first argument, in the direction of the second argument.
>>
>> Often, I find myself wanting specifically the next-higher or next-lower
>> representable value. In these cases, it feels awkward to cook up a number
>> that I know will be higher or lower.
>>
>
> Positive infinity is greater than all real numbers.
> Negative infinity is less than all real numbers.
> (And std::nextafter(+inf, +inf) is +inf; std::nextafter(NaN, anything) is
> NaN; std::nextafter(-inf, +inf) is -DBL_MAX or -LDBL_MAX, depending on the
> computed return type. These outcomes all seem sensible to me.)
>
> [...] `infinity` would be a disastrous choice for integral types, where it
>> returns `0`. (Not that we typically *intentionally use* `nextafter` for
>> integral types, but it may happen in a generic template by accident.)
>>
>
> `std::nextafter(42, [anything])` is already disastrous, because
> `std::nextafter` is not a generic function; it's an overload set. It works
> on floating-point types only. If you pass it an integer, it will convert
> that integer to `double` and give you back the next *`double`* after it,
> not the next integer. (Or the next `long double`, if the second argument is
> a `long double`.)
> https://godbolt.org/z/9YGxv91v7
>


Sorry, I hastily read the reference page
<https://en.cppreference.com/w/cpp/numeric/math/nextafter> and
misinterpreted it. You're right, of course. My comments about integral
types were a distraction.

My main point was really about putting *user intent and usability* first
and foremost, and giving a simple way to pick the direction. Yes, it's
clear that `numeric_limits<T>::infinity` works (and thanks Jonathan for
reminding me how negation works here). It's just that this seems to be
unnecessarily complicated for a use case that is very common, at least for
me.

   - Users need to bring in <limits>, another header.
   - They need to think about whether `infinity` or `max` is best, and
   refresh their memory on how negative infinity works.
   - Even if they do all that, it feels more verbose than it needs to be...
   which means more opportunities for easily overlooked errors.

// Current implementation:
std::nexttoward(x, std::numeric_limits<*long* *double*>::infinity());

// Suggested alternative:
std::next_higher(x);

Maybe the right answer really is just (a) everyone must just learn that
`infinity` is the best choice, and (b) we must bring in numeric limits
every time we want to write this. I could live with that if it's how the
community feels. It's just that the alternative seems more usable and less
error prone to me.

Thanks,
Chip



>
>
>
>> Sometimes I wonder: wouldn't it be more intent-based to provide something
>> like `std::next_higher()` and `std::next_lower()`, with a single argument?
>> Has this ever been proposed?
>>
>
> If you really want to, you can do this with a macro (or inline function or
> whatever):
> #define NEXT_HIGHER(x) std::nextafter((x), +HUGE_VAL)
> #define NEXT_LOWER(x) std::nextafter((x), -HUGE_VAL)
> I don't think that's worth adding to the paper standard, especially given
> the possibility of confusion between similarly named functions.
>
> –Arthur
>

Received on 2024-12-07 17:29:07