What is the difference between the nextafter and the nexttoward functions of the C++ 2011 standard library ?
3 Answers
Since the functions originate from C, they can't be overloaded, which means two different names for functions that do the same but have different parameter(-type)s. Here are the original signatures:
float nextafter(float, float);
float nexttoward(float, long double);
And now the standard just says there should be a few overloads to make things nicer in C++ (ยง26.8 [c.math] p11
):
Moreover, there shall be additional overloads sufficient to ensure:
- If any argument corresponding to a
double
parameter has typelong double,
then all arguments corresponding todouble
parameters are effectively cast tolong double
.- Otherwise, if any argument corresponding to a
double
parameter has typedouble
or an integer type, then all arguments corresponding todouble
parameters are effectively cast todouble
.- Otherwise, all arguments corresponding to
double
parameters are effectively cast tofloat
.See also: ISO C 7.5, 7.10.2, 7.10.6.
-
1A bit confusing to say they can't be overloaded (which isn't true), then in the next sentence to say there are overloads. In fact, C++11 has overloads, and C99 has type-generic macros. โ Mark Gates Mar 29 '22 at 04:47
A crucial difference that isn't apparent from other answers is the return type. With nextafter
, the return type is std::common_type of the from
and to
types. With nexttoward
, the return type is the from
type; since to
is promoted to long double, its type doesn't matter. Some test code is instructive.
Output:
nextafter ( from, to ) vs.
nexttoward( from, to )
float s = 1.401e-45
double d = 4.941e-324
long double q = 3.645e-4951
----- `from` is float
nextafter ( 0.0f, s ) = 1.401e-45 float
nexttoward( 0.0f, s ) = 1.401e-45 float
nextafter ( 0.0f, d ) = 4.941e-324 double
nexttoward( 0.0f, d ) = 1.401e-45 float
nextafter ( 0.0f, q ) = 3.645e-4951 long double
nexttoward( 0.0f, q ) = 1.401e-45 float
----- `from` is double
nextafter ( 0.0, s ) = 4.941e-324 double
nexttoward( 0.0, s ) = 4.941e-324 double
nextafter ( 0.0, d ) = 4.941e-324 double
nexttoward( 0.0, d ) = 4.941e-324 double
nextafter ( 0.0, q ) = 3.645e-4951 long double
nexttoward( 0.0, q ) = 4.941e-324 double
----- `from` is long double
nextafter ( 0.0L, s ) = 3.645e-4951 long double
nexttoward( 0.0L, s ) = 3.645e-4951 long double
nextafter ( 0.0L, d ) = 3.645e-4951 long double
nexttoward( 0.0L, d ) = 3.645e-4951 long double
nextafter ( 0.0L, q ) = 3.645e-4951 long double
nexttoward( 0.0L, q ) = 3.645e-4951 long double
Code:
// Test nextafter and nextforward.
// nextafter's return type is std::common_type( from, to ).
// nexttoward's return type is same as `from`;
// `to` is promoted to long double, which doesn't really have any external effect.
#include <cmath>
#include <iomanip>
#include <iostream>
//------------------------------------------------------------------------------
// For type(), see
// https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c
#include <type_traits>
#include <typeinfo>
#include <memory>
#include <string>
#include <cstdlib>
// for demangling on non-Microsoft platforms
#ifndef _MSC_VER
#include <cxxabi.h>
#endif
template <typename T>
std::string type()
{
using TR = typename std::remove_reference<T>::type;
std::unique_ptr< char, void(*)(void*) > own(
#ifndef _MSC_VER
abi::__cxa_demangle( typeid(TR).name(), nullptr, nullptr, nullptr ),
#else
nullptr,
#endif
std::free
);
std::string r = own != nullptr ? own.get() : typeid(TR).name();
if (std::is_const<TR>::value)
r += " const";
if (std::is_volatile<TR>::value)
r += " volatile";
if (std::is_lvalue_reference<T>::value)
r += "&";
else if (std::is_rvalue_reference<T>::value)
r += "&&";
return r;
}
template <typename T>
std::string type( T x )
{
return type<T>();
}
//------------------------------------------------------------------------------
int main( int argc, char** argv )
{
std::cout << std::setprecision( 4 );
std::cout << "nextafter ( from, to ) vs.\n"
<< "nexttoward( from, to )\n\n";
float s = std::nextafter( 0.0f, 1.0f );
double d = std::nextafter( 0.0, 1.0 );
long double q = std::nextafter( 0.0L, 1.0L );
std::cout << "float s = " << s << '\n';
std::cout << "double d = " << d << '\n';
std::cout << "long double q = " << q << "\n\n";
std::cout << "----- `from` is float\n"; // Result type
auto x1 = std::nextafter ( 0.0f, s ); // float
auto y1 = std::nexttoward( 0.0f, s ); // float
std::cout << "nextafter ( 0.0f, s ) = " << x1 << " " << type( x1 ) << '\n';
std::cout << "nexttoward( 0.0f, s ) = " << y1 << " " << type( y1 ) << "\n\n";
auto x2 = std::nextafter ( 0.0f, d ); // double
auto y2 = std::nexttoward( 0.0f, d ); // float
std::cout << "nextafter ( 0.0f, d ) = " << x2 << " " << type( x2 ) << '\n';
std::cout << "nexttoward( 0.0f, d ) = " << y2 << " " << type( y2 ) << "\n\n";
auto x3 = std::nextafter ( 0.0f, q ); // long double
auto y3 = std::nexttoward( 0.0f, q ); // float
std::cout << "nextafter ( 0.0f, q ) = " << x3 << " " << type( x3 ) << '\n';
std::cout << "nexttoward( 0.0f, q ) = " << y3 << " " << type( y3 ) << "\n\n";
std::cout << "----- `from` is double\n";
auto x4 = std::nextafter ( 0.0, s ); // double
auto y4 = std::nexttoward( 0.0, s ); // double
std::cout << "nextafter ( 0.0, s ) = " << x4 << " " << type( x4 ) << '\n';
std::cout << "nexttoward( 0.0, s ) = " << y4 << " " << type( y4 ) << "\n\n";
auto x5 = std::nextafter ( 0.0, d ); // double
auto y5 = std::nexttoward( 0.0, d ); // double
std::cout << "nextafter ( 0.0, d ) = " << x5 << " " << type( x5 ) << '\n';
std::cout << "nexttoward( 0.0, d ) = " << y5 << " " << type( y5 ) << "\n\n";
auto x6 = std::nextafter ( 0.0, q ); // long double
auto y6 = std::nexttoward( 0.0, q ); // double
std::cout << "nextafter ( 0.0, q ) = " << x6 << " " << type( x6 ) << '\n';
std::cout << "nexttoward( 0.0, q ) = " << y6 << " " << type( y6 ) << "\n\n";
std::cout << "----- `from` is long double\n";
auto x7 = std::nextafter ( 0.0L, s ); // long double
auto y7 = std::nexttoward( 0.0L, s ); // long double
std::cout << "nextafter ( 0.0L, s ) = " << x7 << " " << type( x7 ) << '\n';
std::cout << "nexttoward( 0.0L, s ) = " << y7 << " " << type( y7 ) << "\n\n";
auto x8 = std::nextafter ( 0.0L, d ); // long double
auto y8 = std::nexttoward( 0.0L, d ); // long double
std::cout << "nextafter ( 0.0L, d ) = " << x8 << " " << type( x8 ) << '\n';
std::cout << "nexttoward( 0.0L, d ) = " << y8 << " " << type( y8 ) << "\n\n";
auto x9 = std::nextafter ( 0.0L, q ); // long double
auto y9 = std::nexttoward( 0.0L, q ); // long double
std::cout << "nextafter ( 0.0L, q ) = " << x9 << " " << type( x9 ) << '\n';
std::cout << "nexttoward( 0.0L, q ) = " << y9 << " " << type( y9 ) << "\n\n";
return 0;
}
On my laptop at least (2.3 GHz Core i9, macOS 12), for normal numbers the performance was similar (3.0e-9 sec), but if from
is subnormal, nexttoward
is about 11x slower than nextafter
(5.0e-8 versus 4.5e-9 sec).

- 452
- 5
- 8
Read man page:
The nexttoward() functions do the same as the nextafter() functions, except that they have a long double second argument.

- 485
- 3
- 10
-
4I think you forgot to quote the part about how the second argument is used. โ sblom Aug 21 '12 at 22:16
-
2Without discussing the implications, this is not very helpful. Are there corner cases where the behavior can differ? Is there a performance overhead of converting to long double internally? โ bluenote10 Oct 23 '20 at 09:38