NaN或false为双精度返回值

时间:2014-05-16 14:07:07

标签: c++ nan logical-operators double-precision

我有一个返回double值的函数。在某些情况下,结果为零,并且应在呼叫者路由中相应地处理此结果。我想知道返回零的正确方法是什么(NaN,false等等!)代替双值数字:

double foo(){
    if (some_conditions) {
        return result_of_calculations;
    } else {
        // Which of the following is better?
        return std::numeric_limits<double>::quiet_NaN(); // (1)
        return 0;                                        // (2)
        return false;                                    // (3)
        return (int) 0;                                  // (4)
    }
}

调用例程就像这样:

double bar = foo();
if (bar == 0) {
    // handle the zero case 
} else {
    // handle the non-zero case 
}

#{1}}可以安全地用于#3和#4吗?我可以将它与#2一起使用,还是应该像if (bar == 0)这样做?

如何处理fabs(bar - 0) < EPSILON的情况?我在此网站(例如12)中读到,NaN的比较必须始终为false。该怎么办?

总之,如何为了以后的比较而返回一个假值来代替双倍?

4 个答案:

答案 0 :(得分:3)

您不应将函数计算的实际值与进一步描述结果的附加消息混合,即错误状态。


好方法是:

  1. 自定义结果结构

    struct foo_result_t
    {
        double value;
        bool state;
    };
    
    foo_result_t foo() {
        foo_result_t r;
        r.value = result_of_calculations;
        r.state = some_conditions;
        return r;
    }
    

    这可能是最好的方法,如果您需要更多来自功能的信息,可以轻松扩展。就像说明它为什么会失败一样。

    对于简单的情况,您还可以考虑使用std::pair<double,bool>

    您也可以使用boost::optionallink)。

  2. C ++异常方式

    double foo() {
        if(some_conditions) {
            return result_of_calculations;
        }
        else {
            throw some_exception;
        }
    }
    

    这看起来很优雅,但有时​​您可能希望保持代码异常免费。

  3. 老式的C式方式:

    bool foo(double& result) {
        if(some_conditions) {
            result = result_of_calculations;
            return true;
        }
        else {
            return false;
        }
    }
    

    这可能是最直接的,没有开销(例外,额外的结构)。但它看起来有点奇怪,因为函数试图返回两个值,但其中一个实际上是一个参数。

答案 1 :(得分:1)

没有足够的背景来完全回答这个问题。听起来你希望在some_conditions评估为假时返回哨兵。但是正确的哨兵是什么?回答这个问题主要取决于foo()将在何处以及如何使用。

在大多数情况下,正确的答案都不是上述问题。也许抛出异常会更好。将接口更改为bool foo(double&)bool foo(double*)可能会更好。也许,正如PlasmaHH建议的那样,将其改为boost::optional<double> foo()会更好。

如果哨兵更好,正确的哨兵将取决于价值。假设呼叫者正在添加或相乘。如果它不应该影响结果,则标记应分别为0.0或1.0。如果它应该完全抛弃结果,那么NaN或Inf可能更有意义。

至于EPSILON的问题,这取决于你想如何处理由另一种情况返回的接近零的值。返回文字0.0值将导致双精度与0.0完全比较;计算&#34;应该&#34;但是,0.0的结果可能不会导致0.0

答案 2 :(得分:1)

  

if(bar == 0)是否可以安全地与#3和#4一起使用?

#3#4编译为#2

  

我可以将它与#2一起使用,还是应该像fabs(bar-0)&lt; EPSILON

是(您可以将其与#2一起使用):double bar = 0; =&gt; bar == 0;

然而!如果some_conditions == true分支可以通过某种计算返回(接近)零,并且您需要以相同的方式处理该情况,那么在比较浮点值的相等性时需要使用常用的技巧。

另一方面,如果some_conditions == true分支可能返回零,但该情况的处理方式应与false分支的结果不同,那么您需要使用另一种方法。 Danvil列出了有用的替代品。

至于比较NaN,请使用isnan

答案 3 :(得分:1)

鉴于此示例代码,

double foo(){
    if (some_conditions) {
        return result_of_calculations;
    } else {
        // Which of the following is better?
        return std::numeric_limits<double>::quiet_NaN(); // (1)
        return 0;                                        // (2)
        return false;                                    // (3)
        return (int) 0;                                  // (4)
    }
}

如果情况2,3和4返回相同的值,则很明显设计不是基于C ++的知情视图

因此我建议更改设计。

我通常会做以下事情:

auto can_foo()
    -> bool
{ return (some_conditions); }

auto foo()
    -> double
{
    assert( can_foo() );
    return result_of_calculations;
}

但是,在某些情况下,can_foo尝试进行foo密不可分,在这种情况下,我只会抛出异常:

auto hopefully( bool const cond ) -> bool { return cond; }
auto fail( string const& s ) -> bool { throw runtime_error( s ); }

auto foo()
    -> double
{
    double const result = calculations();
    hopefully( calculations_succeeded() )
        || fail( "foo: calculations failed" );
    return result;
}

foo中抛出异常的替代方法是返回基于Barton&amp; amp;的boost::optional或其他对象。 Nackman Fallible班。使用这种方法,实际投掷与否将被委托给客户端代码。请注意,如果您不关心效率,实施Optional课程是微不足道的:只需使用std::vector作为价值载体。


返回 NaN 并不是一个好主意,因为难以为NaN进行便携式测试。主要是因为主编译器的优化开关使得它们以不符合的方式运行,同时报告它们符合标准。