使用 gmp 的近似值 mpf_class

Approximation using gmp mpf_class

我正在使用 Catch2 编写单元测试。
我想检查两个向量是否相等。使用 gmplib 它们看起来像下面这样:

std::vector<mpf_class> result

由于我 'faking' expected_result 向量,我在测试失败后收到以下消息:

unittests/test.cpp:01: FAILED:
REQUIRE( actual_result == expected_result )
with expansion:
  { 0.5, 0.166667, 0.166667, 0.166667 }
   ==
  { 0.5, 0.166667, 0.166667, 0.166667 }

所以我一直在寻找一个可以为我做近似的函数。
我只是没有成功找到适合我的解决方案。

我找到了一些 Comparison Functions 但它们不适用于我的项目。

编辑: “最小的、可重现的例子就是:

TEST_CASE("DemoTest") { 
// simplified: 
mpf_class a = 1; 
mpf_class b = 6; 

mpf_class actual_result = a / b; 
mpf_class expected_result= 0.16666666667; 

REQUIRE(actual_result == expected_result);
} 

与我的实际应用程序的“唯一”区别是结果存储在向量中。但是因为我只是通过说它是“0.1666666667”来“伪造”结果,所以它可能不再适合 == 了。所以我需要一个采用近似值并比较范围的函数,如 epsilon = +-0.001.

编辑: 实施解决方案后,@Arc 建议它运行良好,直到我有一些不完整的“偶数”值。
所以我有以下值的失败:

actual   0.16666666666666666666700000000000000000000000000000
expected 0.16666666666666665741500000000000000000000000000000

尽管我的“预期”值是这样的:

mpf_class expected = 0.16666666666666666666700000000000000000000000000000

回到我最初的问题,是否有一种方法可以将数字的近似值与类似 +-0.0001 的 epsilon 进行比较,或者解决此问题的最佳方法是什么?

首先,我们需要看一些 Minimal, Reproducible Example 以确定发生了什么。例如,您可以从 test.cpp 中删除一些代码,直到只剩下几行代码,但问题仍然存在。另外,请提供编译和 运行ning 说明。通常,对您的目标做一些解释也可能有所帮助。由于 Catch2 在 GitHub 上可用,因此您无需提供它。

在没有看到代码的情况下,我最多只能猜测您的代码正在尝试使用 == 运算符来比较 mpf_class 中的 mpf_t 类型,这恐怕尚未过载(参见 here). You should compare mpf_ts with the cmp function, since the C type mpf_t is actually an struct containing the pointer to the actual significand limbs. Check some usage examples in the tests/cxx/ directory of GMP (like here)。

我注意到您使用的是非常旧的 GNU MP 4.1 版本,如果可能,您可能想移动到最新版本 6.2.1。此外,对于使用浮点数,建议您使用 GNU MPFR 库而不是 GMP 浮点数。

编辑:我还没有设法 运行 Catch2,但是你的代码的问题是 expected_result 实际上不等于 actual_result。在 GMP mpf_t 中,变量是用 64 位有效位精度创建的(在 64 位机器上),因此除法 a / b 实际上会产生一个打印 0.166666666666666666667 的二进制文件(即数字 1 后的 19 个六) ).尝试用 gmp_printf("%.50Ff\n", actual_result); 打印结果,因为标准 cout 输出只会给你四舍五入到 6 位数字的值:0.166667.

但问题是你不能像 expected_result = 0.166666666666666666667 这样赋值,因为在 C/C++ 中数字常量被解析为 double,因此你必须使用字符串重载归因以获得更高的精确度。

但是你也不能轻易地(或者,一般来说,合理地)创造一个十进制字符串,该字符串将正确转换为由 [=23 给出的完全相同的二进制文件=] 因为十进制到浮点数的转换有微妙之处,例如参见 [​​=37=].

因此,这完全取决于您的应用程序和您打算进行的数值验证类型。如果您知道您的小数验证值对于某些已知精度是正确的,并且如果您将 mpf_t 变量设置为承受精度(例如使用 mpf_set_prec),那么您可以使用公差比较,就像这样。

在 C++ 中(没有 Catch2),它是这样工作的:

#include <iostream>
#include <gmpxx.h>
using namespace std;

int main (void) 
{
    mpf_class a = 1;
    mpf_class b = 6;
    mpf_class actual = a / b;
    mpf_class expected;
    mpf_class tol;
    expected = "0.166666666666666666666666666666667";
    tol = "1e-30";
    cout << "actual " << actual << "\n";
    cout << "expected " << expected << "\n";
    gmp_printf("actual %.50Ff\n", actual);
    gmp_printf("expected %.50Ff\n", expected);
    gmp_printf("tol %.50Ff\n", tol);
    mpf_class diff = expected - actual;
    gmp_printf("diff %.50Ff\n", diff);
    if (abs(actual - expected) < tol)
        cout << "ok\n";
    else
        cout << "nop\n";
    return 0;
}

并使用 -lgmpxx -lgmp 选项进行编译。

它产生输出:

actual 0.166667
expected 0.166667
actual 0.16666666666666666666700000000000000000000000000000
expected 0.16666666666666666666700000000000000000000000000000
tol 0.00000000000000000000000000000100000000000000000000
diff 0.00000000000000000000000000000000033333529249058470
ok

如果我理解 Catch2 很好,如果你将 expected_result 分配给字符串然后与 REQUIRE(abs(actual - expected) < tol) 进行比较应该没问题。