补偿 double/float 错误

Compensating for double/float inaccuracy

我编写了一个数学计算器,它从用户那里接收一个字符串并对其进行解析。它使用双精度数来保存计算时涉及的所有值。解决后,我将其打印出来,并使用 std::setprecision() 确保其输出正确(例如 0.9999999 将在打印输出时变为 1

返回将要输出的字符串:

//return true or false if this is in the returnstring.

if (returnString.compare("True") == 0 || returnString.compare("False") == 0) return returnString;

    //create stringstream and put the answer into the returnString.
    std::stringstream stream;
    returnString = std::to_string(temp_answer.answer);

    //write into the stream with precision set correctly.
    stream << std::fixed << std::setprecision(5) << temp_answer.answer;


    return stream.str();

我知道使用双精度和浮点数时的准确性问题。今天我开始编写代码,以便用户可以比较两个数学字符串。例如,1=1 将评估为 true,2>3 false 等。这通过 运行 我的数学表达式解析器为比较运算符的每一侧工作,然后比较答案。

我现在面临的问题是当用户输入类似 1/3*3=1 的内容时。当然,因为我使用的是双打,解析器将 return 0.999999 作为答案。通常,在解决非比较问题时,如前所述,这会在打印时使用 std::setprecision() 进行补偿。但是,当比较两个双打时,它将 return false as 0.99999!=1。我怎样才能得到它,以便在比较双打时补偿这种不准确性,并且答案 return 正确?这是我用来比较数字本身的代码。

bool math_comparisons::equal_to(std::string lhs, std::string rhs)
{
    auto lhs_ret = std::async(process_question, lhs);
    auto rhs_ret = std::async(process_question, rhs);
    bool ReturnVal = false;

    if (lhs_ret.get().answer == rhs_ret.get().answer)
    {
        ReturnVal = true;
    }

    return ReturnVal;
}

我认为需要进行某种舍入,但我不是 100% 确定如何正确完成它。如果这个问题已经得到解决,请原谅我——我通过搜索找不到太多东西。谢谢!

假设answer是一个double,替换这个

lhs_ret.get().answer == rhs_ret.get().answer

abs(lhs_ret.get().answer - rhs_ret.get().answer) < TOL

其中 TOL 是适当的公差值。

永远不要将浮点数与 == 进行比较,而是通过检查绝对差异是否小于给定的公差来进行比较。

有一个难​​点需要说一下:double的精度大约是16位小数。所以你可以设置 TOL=1.0e-16。这仅在您的数字小于 1 时有效。对于 16 位数字,这意味着公差必须与 1 一样大。

因此,您要么假设您的数字小于 10e8 并使用相对较大的公差,例如 10e-8,要么您需要做一些更复杂的事情。

首先考虑:

作为基本的经验法则,double 将是一个大约 16dp 的值,其中 dp 是小数位,或者 1.0e-16。您需要注意,这仅适用于 10.n 小于一 (1) IE 的数字,您必须围绕该事实进行操作,您只能拥有 15dp EG:10.0e-15 等等... 由于计算机以 2 为基数计数,而人们以 10 为基数计数,因此某些值永远无法在 "most" 现代 OS 的位范围内正确表达使用。

这一点可以通过以二进制或基数 2 表示 0.1 是无限长的事实来强调。

因此,永远不要通过 == 运算符比较有理数。相反,通常使用的 "go to" 解决方案是:

您实施了一个"close enough"解决方案。 IE:您将 epsilon 定义为一个值,例如:epsilon = 0.000001 并声明如果 (value a - value b) < epsilon == true。我们所说的是 if a - b is within e,对于我们程序的所有意图和目的,它足够接近可以被视为真实的。

现在为 epsilon 选择一个值,这完全取决于您需要达到的准确度。例如,可以假设与 2D 横向卷轴平台游戏相比,结构工程需要更高的准确性。

您的解决方案是替换代码的第 7 行:

(lhs_ret.get().answer == rhs_ret.get().answer)

abs(lhs_ret.get().answer - rhs_ret.get().answer) < epsilon 其中 abs 是绝对值。 IE 忽略值 lhs.

的符号

对于更多上下文,我强烈推荐这个关于麻省理工学院开放课件的讲座,它以易于理解的方式对其进行了解释。

http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00sc-introduction-to-computer-science-and-programming-spring-2011/unit-1/lecture-7-debugging/