nextafter vs C++ 2011中的nexttoward函数?

Vin*_*ent 17 c++ floating-point c++-standard-library c++11

C++ 2011标准库的nextafter和nexttoward函数有什么区别?

Xeo*_*Xeo 15

由于函数源自C,因此它们不能被重载,这意味着对于执行相同但具有不同参数(-type)s的函数有两个不同的名称.以下是原始签名:

float nextafter(float, float);
float nexttoward(float, long double);
Run Code Online (Sandbox Code Playgroud)

现在标准只是说应该有一些重载使C++(§26.8 [c.math] p11)中的东西变得更好:

此外,还应有足够的额外过载来确保:

  1. 如果对应于double参数的任何参数具有类型,long double,则对应于double参数的所有参数都被有效地转换为long double.
  2. 否则,如果对应于double参数的任何参数具有类型double或整数类型,则对应于double参数的所有参数都被有效地转换为double.
  3. 否则,所有与参数对应的double参数都被有效地转换为float.

另见:ISO C 7.5,7.10.2,7.10.6.


Mar*_*tes 6

与其他答案不明显的一个关键区别是返回类型。对于nextafter,返回类型是fromto类型的 std::common_type 。对于nexttoward,返回类型是fromtype ;由于to提升为 long double,因此其类型并不重要。一些测试代码具有指导意义。

输出:

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
Run Code Online (Sandbox Code Playgroud)

代码:

// 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
// /sf/ask/5730931/
#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;
}
Run Code Online (Sandbox Code Playgroud)

至少在我的笔记本电脑上(2.3 GHz Core i9、macOS 12),对于正常数字,性能相似(3.0e-9 秒),但如果from低于正常值,nexttoward则比nextafter(5.0e-8 与 4.5e-9)慢约 11 倍秒)。