【问题标题】:Comparing a double and int, without casting or conversion比较 double 和 int,无需转换或转换
【发布时间】:2012-05-03 03:17:34
【问题描述】:

在我们拥有的一个 C++ 模块中,我们有一种表达式评估语言。

                                \
EVDataElement NAME::eval(   const EvalContext &ec,                          \
                        const bool recursiveFlag,                       \
                        EVEvaluatorTraceFormatter * trace )             \
{                                                                           \
/*  EVTimer timer("(DECLARE_REL_EVAL)","eval","*", "", 1,1, 3);      */     \
    EVDataElement val (                                                     \
        (left->eval(ec, recursiveFlag, trace))                              \
        OP (right->eval(ec, recursiveFlag, trace)) );                       \
    return val;                                                             \
}

DECLARE_REL_EVAL(oLT,<)
DECLARE_REL_EVAL(oLE,<=)
DECLARE_REL_EVAL(oGT,>)
DECLARE_REL_EVAL(oGE,>=)
DECLARE_REL_EVAL(oEQ,==)
DECLARE_REL_EVAL(oNE,!=)

该模块允许设置某些配置规则。

所以,如果数据库中有一条规则说 field1 - field2 > param1,它会通过传递给上面的表达式语言来验证这个条件并返回一个结果。

我们现在面临的问题是 param1 = 3, and field1 = 6.15, and field2 = 3.15

它说结果是真的。我认为这是因为 6.15 和 3.15 的差异导致 3.00

当 3.00 与 3 比较时,它认为 3.00 更大。有没有办法解决这个问题?

我说我们不能使用强制转换的原因是因为我们永远不知道左右可能会出现什么数据类型。我希望这个问题有意义。

【问题讨论】:

  • 你怎么知道值是6.15?你确定它不是 6.150000000000001?
  • 对于许多比较浮点数的情况,您至少应该找到绝对差异并进行比较以检查它是否小于适合您需要的一些小 epsilon(例如 1e-10)。
  • @MooingDuck:我不知道它是如何存储在内存中的,但是该值是从数据库中读取的,并且在数据库中存储的是 6.15。我试图找到从数据库中读取值的代码,但我认为它将它读取为双精度值。所以,即使在内存中,也应该是 6.150000

标签: c++ expression precision


【解决方案1】:

在处理不同原始类型的值时,您将获得“通常的转换”。我认为没有办法解决这个问题。

如果您将ints 与doubles 进行比较,则在确定两个值是否“足够接近”时,您将需要提出您想要使用的任何规则。您可以考虑使用std::modf function (in &lt;cmath&gt;) 进行比较。

考虑:

#include <iostream>
#include <cmath>

int main()
{
    double d = 6.15 - 3.15;
    std::cout << std::boolalpha;
    std::cout << "d == 3.0: " << (d == 3.0) << '\n';
    double i;
    d = std::modf(d, &i);
    std::cout << "i = " << i << ", d = " << d << '\n';
    std::cout << "i == 3.0: " << (i == 3.0) << '\n';
}

在默认设置下使用 Visual Studio 2010(即 NOT 使用 fastmath)我得到:

d == 3.0: false
i = 3, d = 4.44089e-016
i == 3.0: true

3.0 可以用二进制浮点数学精确表示,但6.15 - 3.15 在二进制浮点数学中不是3.0


已经有两个参考文献“What Every Computer Scientist Should Know About Floating-Point Arithmetic”描述了二进制浮点数学的工作原理,以及它如何并不总是符合人类的期望。要记住的要点是,您几乎不想比较两个浮点数是否相等,尤其是如果这些数字中的一个(或两个)是数学运算的结果。

但是,在您的情况下,您尝试将 doubleint 进行比较,我必须假设您想要 一些 舍入。您可能需要考虑将3.1 等同于3。你可能不会。我真的不知道。

如果您要使用小学教授的四舍五入约定(四舍五入为 0.5 或更高),您可能会执行以下操作:

#include <iostream>
#include <cmath>

int main()
{
    double d = 6.15 - 3.15;
    std::cout << std::boolalpha;
    std::cout << "d == 3.0: " << (d == 3.0) << '\n';
    // note:  this rounds negative numbers the wrong direction
    std::cout << "d is 'close enough' to 3.0: " << (std::floor(d + 0.5) == 3.0) << '\n';
}

还有更复杂的可能性,包括论文中描述的一种。

【讨论】:

  • 我尝试了这个,并使用了 double d = 6.1 - 3.1 并得到了 i = 2 and d = 1 。这说明什么?它未能与 3.0 相等,事实上它说它小于 3.0
  • @rorymustang: 如果std::modf(6.1 - 3.1, &amp;i) 返回1(即d = 1,那么我怀疑d 小于,但非常接近1.0。尤其是因为d + 2 &lt; 3.0 .
  • @rorymustang:您可能没有意识到浮点数在显示时通常是四舍五入的(这是与“计算机中的浮点数学计算与您手动计算的不同”不同的问题)。 std::cout ,, 1.23456789 可能会显示类似 1.23457 的内容。
  • 是的,我使用了 setprecision() 并了解了如何显示所有数字。
  • 糟糕。那应该是std::cout &lt;&lt; 1.23456789;或者,更重要的是,std::cout &lt;&lt; .9999999 可能会显示 1
【解决方案2】:

如果您想将field1 - field2 &gt; param1 用于param1 = 3field1 = 6.15field2 = 3.15,那么您将需要使用infinite precision 算术。

请阅读上述Floating-Point Arithmetic上的论文。

【讨论】:

  • 嘿,我正在尝试找到一种在 C++ 中使用无限精度的方法。如何显示完整的精度?我试过用setprecisioncplusplus.com/reference/iostream/manipulators/setprecision,但是solaris下的编译器好像不能识别这个函数。
  • @rorymustang86:C++ 标准库不提供无限精度库。 GNU 在 LGPL 下提供 GNU GMP (gmplib.org) 和 GNU MPFR (mpfr.org)。您可以在 oonumerics.org/oon 找到其他库的列表(查找“多精度、任意精度数据类型”)。
猜你喜欢
  • 1970-01-01
  • 2011-02-23
  • 2015-07-04
  • 2011-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多