当舍入到n个有效十进制数字时确定两个数字是否几乎相等的函数

时间:2009-02-17 18:49:21

标签: python math floating-point numpy

我被要求测试第三方提供的图书馆。众所周知,该库对 n 有效数字是准确的。可以安全地忽略任何不太重要的错误。我想写一个函数来帮助我比较结果:

def nearlyequal( a, b, sigfig=5 ):

此函数的目的是确定两个浮点数(a和b)是否大致相等。如果a == b(完全匹配),或者当以十进制写入时舍入为 sigfig 有效数字时,a和b具有相同的值,则该函数将返回True。

有人可以建议一个好的实施吗?我写了一个迷你单元测试。除非您在我的测试中看到错误,否则一个好的实现应该通过以下内容:

assert nearlyequal(1, 1, 5) 
assert nearlyequal(1.0, 1.0, 5) 
assert nearlyequal(1.0, 1.0, 5) 
assert nearlyequal(-1e-9, 1e-9, 5) 
assert nearlyequal(1e9, 1e9 + 1 , 5) 
assert not nearlyequal( 1e4, 1e4 + 1, 5) 
assert nearlyequal( 0.0, 1e-15, 5 ) 
assert not nearlyequal( 0.0, 1e-4, 6 ) 

附加说明:

  1. 值a和b可以是int,float或numpy.float64类型。值a和b将始终为相同类型。转换不会在函数中引入额外的错误,这一点至关重要。
  2. 让我们保持这个数字,所以转换为字符串或使用非数学技巧的函数并不理想。该程序将由一位数学家进行审核,他希望能够证明该功能能够完成它应该做的事情。
  3. 速度......我必须比较很多数字,所以越快越好。
  4. 我有numpy,scipy和标准库。对我来说,任何其他事情都很难获得,特别是对于项目的这么小的一部分。

10 个答案:

答案 0 :(得分:55)

从Python 3.5开始,执行此操作的标准方法(使用标准库)使用math.isclose函数。

它有以下签名:

isclose(a, b, rel_tol=1e-9, abs_tol=0.0)

绝对容错的用法示例:

from math import isclose
a = 1.0
b = 1.00000001
assert isclose(a, b, abs_tol=1e-8)

如果您希望精确度为 n 有效数字,只需将最后一行替换为:

assert isclose(a, b, abs_tol=10**-n)

答案 1 :(得分:19)

assert_approx_equal中有一项功能numpy.testing(来源here) ,这可能是一个很好的起点。

def assert_approx_equal(actual,desired,significant=7,err_msg='',verbose=True):
    """
    Raise an assertion if two items are not equal up to significant digits.

    .. note:: It is recommended to use one of `assert_allclose`,
              `assert_array_almost_equal_nulp` or `assert_array_max_ulp`
              instead of this function for more consistent floating point
              comparisons.

    Given two numbers, check that they are approximately equal.
    Approximately equal is defined as the number of significant digits
    that agree.

答案 2 :(得分:8)

这是一个拍摄。

def nearly_equal(a,b,sig_fig=5):
    return ( a==b or 
             int(a*10**sig_fig) == int(b*10**sig_fig)
           )

答案 3 :(得分:4)

我相信你的问题定义不够好,你提出的单元测试证明了这一点:

如果通过'round to N sig-fig decimal places'表示'小数点右边的'N位小数',那么测试assert nearlyequal(1e9, 1e9 + 1 , 5)应该会失败,因为即使你将1000000000和1000000001舍入到0.00001准确度,它们仍然不同。

如果通过'round to N sig-fig decimal places'表示'N个最高有效数字,无论小数点',那么测试assert nearlyequal(-1e-9, 1e-9, 5)应该会失败,因为0.000000001和-0.000000001是完全一样的从这个角度来看是不同的。

如果您的意思是第一个定义,那么此页面上的第一个答案(由Triptych提供)是好的。 如果你的意思是第二个定义,请说出来,我保证会考虑: - )

答案 4 :(得分:3)

已经有很多很棒的答案,但这是一个想法:

def closeness(a, b):
  """Returns measure of equality (for two floats), in unit
     of decimal significant figures."""
  if a == b:
    return float("infinity")
  difference = abs(a - b)
  avg = (a + b)/2
  return math.log10( avg / difference )


if closeness(1000, 1000.1) > 3:
  print "Joy!"

答案 5 :(得分:2)

十进制中的“重要数字”是调整小数点并截断为整数的问题。

>>> int(3.1415926 * 10**3)
3141
>>> int(1234567 * 10**-3)
1234
>>>

答案 6 :(得分:1)

浮点数是一个相当常见的问题。我根据Demmel [1]第1.5节的讨论解决了这个问题。 (1)计算舍入误差。 (2)检查舍入误差是否小于某个epsilon。我有一段时间没有使用python,只有版本2.4.3,但我会尝试正确。

步骤1.舍入错误

def roundoff_error(exact, approximate):
    return abs(approximate/exact - 1.0)

步骤2.浮点平等

def float_equal(float1, float2, epsilon=2.0e-9):
    return (roundoff_error(float1, float2) < epsilon)

此代码存在一些明显的缺陷。

  1. 如果确切的值为零,则除以零错误。
  2. 不验证参数是浮点值。
  3. 修订版1。

    def roundoff_error(exact, approximate):
        if (exact == 0.0 or approximate == 0.0):
            return abs(exact + approximate)
        else:
            return abs(approximate/exact - 1.0)
    
    def float_equal(float1, float2, epsilon=2.0e-9):
        if not isinstance(float1,float):
            raise TypeError,"First argument is not a float."
        elif not isinstance(float2,float):
            raise TypeError,"Second argument is not a float."
        else:
            return (roundoff_error(float1, float2) < epsilon)
    

    那好一点。如果精确值或近似值为零,则误差等于另一个值。如果提供了除浮点值之外的某些内容,则会引发TypeError。

    此时,唯一困难的是为epsilon设置正确的值。我在版本2.6.1的文档中注意到sys.float_info中有一个epsilon属性,所以我会使用两倍的值作为默认的epsilon。但正确的值取决于您的应用程序和算法。

    [1] James W. Demmel, Applied Numerical Linear Algebra ,SIAM,1997。

答案 7 :(得分:1)

Oren Shemesh解决了问题的部分问题,但还有更多:

断言几乎等于(0.0,1e-15,5)

也失败了第二个定义(这就是我在学校学到的定义。)

无论你看多少位数,0都不等于非零。如果您的案例的正确答案为零,这可能会成为此类测试的头疼问题。

答案 8 :(得分:1)

B. Dawson(使用C ++代码)有一个有趣的解决方案 在"Comparing Floating Point Numbers"。当所述数字表示为无符号整数时,他的方法依赖于两个数字的严格IEEE表示和强制字典排序。

答案 9 :(得分:0)

有很多方法可以比较两个数字,看看它们是否同意N个有效数字。粗略地说,你只是想确保它们的差异小于被比较的两个数字中最大值的10 ^ -N倍。这很容易。

但是,如果其中一个数字为零怎么办?当与零进行比较时,相对差异或有效数字的整个概念会下降。要处理这种情况,你需要有一个绝对差异,应该与相对差异指定不同。

我在这篇博客文章中讨论了比较浮点数的问题 - 包括处理零的具体情况:

http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/