【问题标题】:Checking if float is equivalent to an integer value in python检查浮点数是否等于python中的整数值
【发布时间】:2022-01-01 04:38:23
【问题描述】:

在 Python 3 中,我正在检查给定值是否为三角形,也就是说,对于某个正整数 n,它可以表示为 n * (n + 1) / 2

我可以写吗:

import math

def is_triangular1(x):
    num = (1 / 2) * (math.sqrt(8 * x + 1) - 1)
    return int(num) == num

或者我需要在公差范围内进行检查吗?

epsilon = 0.000000000001
def is_triangular2(x):
    num = (1 / 2) * (math.sqrt(8 * x + 1) - 1)
    return abs(int(num) - num) < epsilon

我检查了这两个函数返回相同的结果 x 最多 1,000,000。但我不确定一般来说int(x) == x 是否总是能正确确定一个数字是否为整数,因为例如 5 表示为 4.99999999999997 等情况。

据我所知,如果我在 C 中使用第二种方法是正确的,但我不确定 Python 3。

【问题讨论】:

  • 我知道您在询问浮点数和整数,但您可能会发现以下 Q 有用 stackoverflow.com/questions/5595425/…
  • 两个函数的返回值可能相同,但由于某种原因,它们对我尝试过的所有数字都返回 True。
  • @Jochen,你在使用 Python3 吗?在 Python2 中 1/2==0 所以 num 将永远只是 0
  • @gnibbler:对了,我应该仔细阅读

标签: python floating-point


【解决方案1】:

python浮点型中有is_integer函数:

>>> float(1.0).is_integer()
True
>>> float(1.001).is_integer()
False
>>> 

【讨论】:

    【解决方案2】:

    您的两个实现都有问题。实际上可能会发生您最终得到类似4.999999999999997 的情况,因此不能选择使用int()

    我会采用完全不同的方法:首先假设您的数字是三角形的,然后计算 n 在这种情况下会是什么。在第一步中,您可以大方地四舍五入,因为只有当数字实际上是 三角形时才需要得到正确的结果。接下来,计算此nn * (n + 1) / 2,并将结果与​​x 进行比较。现在,您正在比较两个整数,因此没有任何不准确之处。

    n的计算可以通过展开来简化

    (1/2) * (math.sqrt(8*x+1)-1) = math.sqrt(2 * x + 0.25) - 0.5
    

    并利用它

    round(y - 0.5) = int(y)
    

    对于积极的y

    def is_triangular(x):
        n = int(math.sqrt(2 * x))
        return x == n * (n + 1) / 2
    

    【讨论】:

    • round(y - 0.5) == int(y) 似乎并不总是正确的:在 Python 3 中,round(1-0.5)==0,但 int(1)==1
    • @Sunny:有趣——round() 的行为在 Python 3.x 中发生了变化。但是由于我们只想对非常接近整数的数字进行四舍五入,所以我们不必担心小数部分正好是 0.5 的情况。正如我之前所说,我们可以对四舍五入非常宽容,因为我们只需要在数字实际上是三角形的情况下正确处理。这就是为什么省略 0.25 也是可以的。
    • 你可以列出所有三角数 ((x*x+x)/2 for x in count()) 并测试该函数是否为这些提供 True 为其他所有内容提供 False ......它确实,因为 Python 可以将所有数字变成整数。
    【解决方案3】:

    你会想要做后者。在Python 3 编程中,给出了以下示例作为最准确的比较方式

    def equal_float(a, b):
        #return abs(a - b) <= sys.float_info.epsilon
        return abs(a - b) <= chosen_value #see edit below for more info
    

    此外,由于 epsilon 是“机器可以区分两个浮点数的最小差值”,因此您需要在函数中使用

    编辑:在阅读了下面的 cmets 之后,我回顾了这本书,它特别指出“这是一个简单的函数,用于比较浮点数与机器精度极限的相等性”。我相信这只是将浮点数与极端精度进行比较的一个例子,但事实上,许多浮点数计算都会引入错误,这应该很少使用。我将其描述为在我的答案中进行比较的“最准确”的方式,这在某种意义上是正确的,但在将浮点数或整数与浮点数进行比较时很少有什么意图。根据函数的“问题域”而不是使用 sys.float_info.epsilon 选择一个值(例如:0.00000000001)是正确的方法。

    感谢 S.Lott 和 Sven Marnach 的更正,如果我引导任何人走错路,我深表歉意。

    【讨论】:

    • 不要为这个问题工作:5 - 4.999999999999997 &lt;= sys.float_info.epsilon 是假的(至少在我的 python3.2 @64 位上)
    • 对了一半。不要使用sys.float_info.epsilon。使用特定于问题域的值。
    • @S-Lott 为什么不用 sys.float_info.epsilon ?
    • @Sunny88:做代数。您的浮点值表示错误可能大于sys.float_info.epsilonepsilon = 0.000000000001 更可能是正确的,因为它取决于您的问题域中的某些内容,而不是浮点表示的相当任意的属性。
    • @Dan:您的编辑只修复了部分问题。您需要针对某个 epsilon 检查 relative 错误,而此 epsilon 不一定是 sys.float_info.epsilon。也许你想阅读standard reference on this topic
    【解决方案4】:

    Python 确实有一个 Decimal 类(在 the decimal module 中),您可以使用它来避免浮点数的不精确性。

    【讨论】:

    • 好答案,对我来说。我希望使用 Decimal 代替 float 没有任何副作用。
    【解决方案5】:

    浮点数可以精确地表示其范围内的所有整数 - 浮点相等只有在您关心点后的位时才会变得棘手。因此,只要您的公式中的所有计算都为您感兴趣的情况返回整数,int(num) == num 是完全安全的。

    因此,我们需要证明,对于任何三角数,您所做的每一项数学运算都可以用整数算术来完成(任何非整数的结果都必须暗示 x 不是三角数):

    首先,我们可以假设 x 必须是一个整数 - 这在“三角数”的定义中是必需的。

    在这种情况下, 8*x + 1 也将是一个整数,因为整数在 + 和 * 下是封闭的。

    math.sqrt() 返回浮点数;但如果 x 是三角形的,那么平方根将是一个整数 - 即,再次精确表示。

    因此,对于所有应在函数中返回 true 的 x,int(num) == num 将为 true,因此您的 istriangular1 将始终有效。正如问题的 cmets 中提到的,唯一的症结在于 Python 2 默认情况下以与 C 相同的方式进行整数除法 - int/int => int,如果结果不能完全表示为 int,则截断.所以,1/2 == 0。这在 Python 3 中是固定的,或者通过使用该行

    from __future__ import division
    

    靠近代码顶部。

    【讨论】:

      【解决方案6】:

      我认为模块 decimal 是你需要的

      【讨论】:

      • 想解释一下这个模块将如何帮助 OP(或任何 Google 受众)或至少发布一个链接?
      • @BlackVegetable 绝对没有错。但是相对于我将丢失的大量帖子,我可能认为不值得写一个详细的答案来重新解释文档中已经解释的内容。有时,仅显示方向很有用。
      • 很公平,您的答案似乎与其他人的方向不同。我希望我可以点击单词 decimal 并获得指向该模块的链接。 (它甚至可能为您赢得一票!)
      • 我有一个原则:赌别人的智商。那天,我可能很累,没有精力解释 decimal 管理精度问题,我认为每个人都受过足够的 Python 培训,知道在哪里可以找到该模块的文档,或者通过单击 帮助按钮,在python.org网站上(而且每个人都有他/她的偏好) - 关于upvote,哦,我还有一些其他答案在几个线程中没有任何意义,我仍然睡得很好。跨度>
      【解决方案7】:

      您可以将您的数字四舍五入,例如小数点后 14 位或更少:

       >>> round(4.999999999999997, 14)
       5.0
      

      PS:双精度约为小数点后 15 位

      【讨论】:

        【解决方案8】:

        很难与标准争论。

        在 C99 和 POSIX 中,将浮点数舍入为 int 的标准由 nearbyint() 定义。重要的概念是舍入方向和特定于语言环境的舍入约定。

        假设约定为common rounding,这与Python中的C99约定相同:

        #!/usr/bin/python
        
        import math
        
        infinity = math.ldexp(1.0, 1023) * 2
        
        def nearbyint(x): 
           """returns the nearest int as the C99 standard would"""
        
           # handle NaN
           if x!=x:
               return x      
        
           if x >= infinity:
               return infinity
        
           if x <= -infinity:
               return -infinity
        
           if x==0.0:
               return x
        
           return math.floor(x + 0.5)
        

        如果您想更好地控制舍入,请考虑使用Decimal module 并选择您希望采用的舍入约定。例如,您可能想使用Banker's Rounding

        一旦你决定了约定,四舍五入到一个 int 并与另一个 int 进行比较。

        【讨论】:

          【解决方案9】:

          考虑使用 NumPy,它们会在后台处理所有事情。

          import numpy as np

          result_bool = np.isclose(float1, float2)

          【讨论】:

            【解决方案10】:

            Python 具有无限的整数精度,但浮点精度只有 53 位。当你对一个数字求平方时,它需要的位数增加一倍。这意味着原始数字的 ULP 是(大约)平方根 ULP 的两倍。

            您开始遇到大约 50 位左右的数字问题,因为无理根的小数表示与最接近的整数之间的差异可能小于 ULP。即使在这种情况下,检查您是否在容忍范围内也会弊大于利(通过增加误报的数量)。

            例如:

            >>> x = (1 << 26) - 1
            >>> (math.sqrt(x**2)).is_integer()
            True
            >>> (math.sqrt(x**2 + 1)).is_integer()
            False
            >>> (math.sqrt(x**2 - 1)).is_integer()
            False
            
            >>> y = (1 << 27) - 1
            >>> (math.sqrt(y**2)).is_integer()
            True
            >>> (math.sqrt(y**2 + 1)).is_integer()
            True
            >>> (math.sqrt(y**2 - 1)).is_integer()
            True
            >>> (math.sqrt(y**2 + 2)).is_integer()
            False
            >>> (math.sqrt(y**2 - 2)).is_integer()
            True
            >>> (math.sqrt(y**2 - 3)).is_integer()
            False
            

            因此,您可以稍微修改问题的表述。如果整数x 是一个三角数,则存在一个整数n 使得x = n * (n + 1) // 2。得到的二次方为n**2 + n - 2 * x = 0。您只需要知道判别式1 + 8 * x 是否是一个完美的正方形。从 python 3.8 开始,您可以使用 math.isqrt 计算整数的整数平方根。在此之前,您可以使用 Wikipedia 中的一种算法,在 SO here 上实现。

            因此,您可以使用以下单线完全停留在 python 的无限精度整数域中:

            def is_triangular(x):
                return math.isqrt(k := 8 * x + 1)**2 == k
            

            现在你可以这样做了:

            >>> x = 58686775177009424410876674976531835606028390913650409380075
            >>> math.isqrt(k := 8 * x + 1)**2 == k
            True
            >>> math.isqrt(k := 8 * (x + 1) + 1)**2 == k
            False
            
            >>> math.sqrt(k := 8 * x + 1)**2 == k
            False
            

            第一个结果是正确的:本例中的x 是一个用n = 342598234604352345342958762349 计算的三角数。

            【讨论】:

              【解决方案11】:

              Python 仍然使用与 C 相同的浮点表示和运算,因此第二种方法是正确的。

              【讨论】:

                【解决方案12】:

                在底层,Python 的浮点类型是 C 的双精度类型。

                最可靠的方法是获取最接近 num 的整数,然后测试该整数是否满足您所追求的属性:

                import math
                def is_triangular1(x):
                    num = (1/2) * (math.sqrt(8*x+1)-1 )
                    inum = int(round(num))
                    return inum*(inum+1) == 2*x  # This line uses only integer arithmetic
                

                【讨论】:

                • 为什么是 inum = int(round(num)) 而不仅仅是 inum = int(num) 或 inum = round(num)?
                • @Sunny88: int(num) 截断而不是四舍五入(因此 0.9999 将变为 0)。 round(num) 舍入但返回浮点数。
                • 显然在python 3中round()的返回值发生了变化。现在如果只有一个参数,它是整数。
                猜你喜欢
                • 1970-01-01
                • 2013-11-29
                • 2013-12-26
                • 1970-01-01
                • 2011-08-13
                • 2022-01-13
                • 2023-03-14
                相关资源
                最近更新 更多