【问题标题】:Simple way to compare doubles比较双打的简单方法
【发布时间】:2013-04-23 23:21:53
【问题描述】:

我正在编写一个数字代码,它需要在双精度数字之间进行广泛(并且可能是快速)比较。我比较两个数字 A 和 B 的解决方案包括将 A 向左(或向右)移动一个 epsilon 并检查结果是否大于(或小于)B。如果是,则两个双精度数相同。 (负数或零数需要额外编码)。

这是比较函数:

#define S_
inline double s_l (double x){
    if(x>0){return 0.999999999*x;}
    else if(x<0){return 1.00000001*x;}
    else {return x-0.000000000001;}
}
inline double s_r (double x){
    if(x>0){return 1.00000001*x;}
    else if(x<0){return 0.999999999*x;}
    else{return x+0.000000000001;}
}
inline bool s_equal (double x,double y){
    if(x==y){return true;}
    else if(x<y && s_r(x)>y){return true;}
    else if(x>y && s_l(x)<y){return true;}
    else{return false;}
}
#endif

由于这是 MonteCarlo 算法的一部分,并且 s_equal(x,y) 被调用了数百万次,我想知道是否有更好或更快的编码方式,可以简单理解。

【问题讨论】:

  • 我做了类似 abs( (x-y)/x )
  • “几乎相等”是一种先进的技术,不应轻易采取。例如,如果a 几乎等于b 并且b 几乎等于c,则并不意味着a 几乎等于c。这可能会导致意想不到的并发症。
  • @brianbeuning 考虑将其发布为答案。这是一个非常与所提出的算法非常接近的匹配,并且通过删除所有 if 分支,它应该比所提出的要快得多。

标签: c++ compare double floating


【解决方案1】:

我做了类似 abs( (x-y)/x )

如果两个值都很大或很小,则需要除以 x。

【讨论】:

  • 感谢您的回复!我想过这种实现,但意识到它可能很危险:1)当x为0时,它会给出NaN 2)当y为0时,无论x如何接近0,lhs都等于1。包括为了摆脱许多 if 语句,解决方案中的“abs”对我来说似乎也很自然,但处理 0 附近的数字似乎并不那么明显。你有没有想过这个问题?你是怎么解决的?
  • abs( ((x == 0.0) ? y : ((y == 0.0) ? x : (x-y)/x)) )
【解决方案2】:

我惊讶地发现通过避免所有双精度数学可以显着加快速度:

#define S_L(x) (x)+((x)<0?1024:-1024)
#define S_R(x) (x)+((x)<0?-1024:1024)
#define S_EQUAL(x,y) (S_L(x)<(y) && S_R(x)>(y))

double foo;                                                                      
double bar;                                                                      
long *pfoo;                                                                      
long *pbar;                                                                      

pfoo = (long*)&foo;                                                              
pbar = (long*)&bar;          

double result1 = S_R(*pfoo);
double result2 = S_L(*pbar);
bool result3 = S_EQUAL(*pfoo, *pbar);

(在测试中,我在 -1M 和 1M 之间对随机生成的双精度进行操作,每个操作执行 100M 次,每次迭代使用不同的输入。每个操作在一个独立的循环中计时,比较系统时间 - 而不是墙时间。包括循环开销和随机数生成,这个解决方案快了大约 25%。)

一个警告:这里有很多依赖于你的硬件,你的双打的范围,你的优化器的行为等等。当你开始怀疑你的编译器时,这样的陷阱很常见。我很震惊地看到这对我来说有多快,因为我一直被告知整数和浮点单元在硬件上保持如此独立,以至于从一个到另一个的位传输总是需要硬件内存操作。谁知道这对你有多好。

您可能需要稍微使用一些神奇的数字(1024s)才能让它按照您的意愿去做 - 如果可能的话。

【讨论】:

  • 谢谢。这是一个有趣的建议,但令人恐惧。代码必须由社区中的任何人移植和编译。我宁愿牺牲一些速度来减少出错的可能性!
【解决方案3】:

如果您使用的是 C++11,那么您可以使用新的 math 库函数,例如:

bool isgreater(float x, float y)

更多关于std::isgreater 的文档可以在here 获得。

否则,总会有is_equal 在 boost 中。此外,SO 已经有一堆相关的(不确定是否相同)问题,例如 hereherehere

【讨论】:

  • 谢谢。我不确定这是否能解决我的问题。我试图找到 isgreater 或 is_equal 的定义,但无法查看它们是否进行 epsilon 比较,如果是,则精度如何。关于其他线程,我确实搜索了它们,但发现大部分讨论都是关于“固定”epsilon 与 abs(x-y)
猜你喜欢
  • 1970-01-01
  • 2013-10-09
  • 1970-01-01
  • 2013-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-15
相关资源
最近更新 更多