我有一个返回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
的情况?我在此网站(例如1,2)中读到,NaN的比较不必须始终为false。该怎么办?
总之,如何为了以后的比较而返回一个假值来代替双倍?
答案 0 :(得分:3)
您不应将函数计算的实际值与进一步描述结果的附加消息混合,即错误状态。
好方法是:
自定义结果结构
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::optional
(link)。
C ++异常方式
double foo() {
if(some_conditions) {
return result_of_calculations;
}
else {
throw some_exception;
}
}
这看起来很优雅,但有时您可能希望保持代码异常免费。
老式的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进行便携式测试。主要是因为主编译器的优化开关使得它们以不符合的方式运行,同时报告它们符合标准。