浮点算术错误

Ale*_*lex 6 python floating-point precision double ieee-754

我正在使用以下函数来近似函数的导数:

def prime_x(f, x, h):

    if not f(x+h) == f(x) and not h == 0.0: 
        return (f(x+h) - f(x)) / h
    else:
        raise PrecisionError
Run Code Online (Sandbox Code Playgroud)

作为一个测试,我路过ffxx为3.0.在哪里fx:

def fx(x):

    import math
    return math.exp(x)*math.sin(x)
Run Code Online (Sandbox Code Playgroud)

哪个有exp(x)*(sin(x)+cos(x))衍生物.现在,根据谷歌和我的计算器

exp(3)*(sin(3)+cos(3)) = -17.050059.

到现在为止还挺好.但是当我决定用小值测试函数时,h我得到了以下结果:

print prime_x(fx, 3.0, 10**-5)
-17.0502585578
print prime_x(fx, 3.0, 10**-10)
-17.0500591423
 print prime_x(fx, 3.0, 10**-12)
-17.0512493014
print prime_x(fx, 3.0, 10**-13)
-17.0352620898
print prime_x(fx, 3.0, 10**-16)
__main__.PrecisionError: Mantissa is 16 digits
Run Code Online (Sandbox Code Playgroud)

当h减小时(某个点之后),为什么误差会增加?我期待相反,直到f(x+h)等于f(x).

Eri*_*hil 7

浮点运算(以及整数运算和定点运算)具有一定的粒度:值只能改变一定的步长.对于IEEE-754 64位二进制格式,该步长约为该值的2 -52倍(约2.22•10 -16).这对于物理测量来说非常小.

但是,当你使h非常小时,f(x)和f(x + h)之间的差异与步长相比并不是很大.差异只能是步长的整数倍.

当导数为d时,f(x)的变化约为h · d.即使您以浮点格式计算f(x)和f(x + h),它们的差值的测量值也必须是步长s的倍数,因此它必须是圆的(hd/小号)• 小号,其中round(Ý)是Ý取整到最接近的整数.显然,当你使h小时,hd/s 较小,因此将其舍入为整数的效果相对较大.

另一种观察方式是,对于给定的f(x),在计算f(x)周围的值时存在一定量的误差.当你使h小时,f(x + h)-f(x)变小,但误差保持不变.因此,误差相对于h增加.


Mar*_*som 6

当您减去两个几乎相同的数字时,结果的精度远低于任何一个输入.这降低了整体结果的精度.

假设您有以下两个数字,小数点后15位:

  1.000000000000001
- 1.000000000000000
= 0.000000000000001
Run Code Online (Sandbox Code Playgroud)

看看发生了什么?结果只有一个好数字.

  • 实际上,减去两个几乎相等的数字就没有误差(假设是IEEE 754).减去两个几乎相等的数字的结果具有与输入(其有效数字中的相同位数)相同的精度和相同的误差(与理想数学值的差异).**唯一**改变的是**相对误差**,并且只是因为你改变了相对于你的测量值(减法的结果而不是输入)而改变了. (4认同)