c++ - 舍入误差减少?

标签 c++ math floating-point rounding floating-accuracy

考虑以下函数:

#include <iostream>
#include <iomanip>
#include <cmath>
#include <limits>

template <typename Type>
inline Type a(const Type dx, const Type a0, const Type z0, const Type b1)
{
    return (std::sqrt(std::abs(2*b1-z0))*dx)+a0;
}

template <typename Type>
inline Type b(const Type dx, const Type a0, const Type z0, const Type a1)
{
    return (std::pow((a1-a0)/dx, 2)+ z0)/2;
}

int main(int argc, char* argv[])
{
    double dx = 1.E-6;
    double a0 = 1;
    double a1 = 2;
    double z0 = -1.E7;
    double b1 = -10;
    std::cout<<std::scientific;
    std::cout<<std::setprecision(std::numeric_limits<double>::digits10);
    std::cout<<a1-a(dx, a0, z0, b(dx, a0, z0, a1))<<std::endl;
    std::cout<<b1-b(dx, a0, z0, a(dx, a0, z0, b1))<<std::endl;
    return 0;
}

在我的机器上,它返回:

0.000000000000000e+00
-1.806765794754028e-07

而不是 (0, 0)。第二个表达式存在较大的舍入误差。

我的问题是:如何在不改变类型的情况下减少每个函数的舍入误差(我需要保留这 2 个函数声明(但公式可以重新排列):它们来自更大的程序)?

最佳答案

遗憾的是,所有浮点类型都因舍入错误而臭名昭著。没有它,它们甚至不能存储 0.1(您可以使用长除法手工证明这一点:二进制等价物是 0b0.0001100110011001100...)。您可能会尝试一些解决方法,例如将该 pow 扩展为硬编码的乘法,但您最终需要对程序进行编码以预测和最小化舍入误差的影响。这里有几个想法:

  • 永远不要比较浮点值是否相等。我看到的一些替代比较包括:abs(a-b) < delta,或 percent_difference (a,b) < delta 或什至 abs(a/b-1) < delta,其中 delta 是您确定有效的“适当小”的值用于此特定测试。

  • 避免将长数组添加到累加器中;随着累加器变大,数组的末尾可能会因舍入误差而完全丢失。在 Jason Sanders 和 Edward Kandrot 的“Cuda 实例”中,作者建议递归地分别添加每对元素,以便每一步生成的数组大小是前一步的一半,直到获得单元素数组。

关于c++ - 舍入误差减少?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19967113/

相关文章:

java - openxml和Excel 15位数字限制

c++ - 使用 C++ MATLAB API 定义二维数组

c++ - 将 Perl 正则表达式转换为等效的 ECMAScript 正则表达式

c++ - C++ 中的五角大楼项目

algorithm - 如何找到 2 个数字的最高 GCD,使得这 2 个数字来自给定的数字范围?

math - float 学有问题吗?

performance - 正确处理 ISO-Prolog 中的非正规 float

c - 如果所有位都为 0,那么 IEEE float 的值是多少?

c++ - 海湾合作委员会 assembly "+t"

c++ - EXPECT_CALL(mock, f(N)) vs f(K) 后跟 f(N)