在 Python 中比较浮点数时遇到问题

Trouble comparing floats in Python

提问人:Justin 提问时间:11/11/2023 更新时间:11/11/2023 访问量:56

问:

我知道这应该更容易,但我在比较浮点值时遇到了问题。

我有一些非常基本的代码,可以做一些事情:

  1. 任何低于 5 的权重都将添加到light_pkgs。
  2. 任何超过 15 的权重都将添加到heavy_pkgs。
  3. 任何超过 15 的权重都将从原始列表中删除。packages
def process_packages(package_list):
    light_pkgs = []
    heavy_pkgs = []

    light_weight = 5.00
    heavy_weight = 15.00

    for i in package_list:
        if i < light_weight:
            light_pkgs.append(i)

        elif i > heavy_weight:
            heavy_pkgs.append(i)
            packages.remove(i)

    return light_pkgs, heavy_pkgs


packages = [2.4, 3.44, 4.55, 4.9, 5.11, 5.31, 5.61, 5.99, 6.11, 7.34, 7.9, 9, 11.3, 15.1, 15.31, 15.68, 15.9, 16.7, 19.3, 21]
print(process_packages(packages))
print(packages)

我相信由于精度错误,我的代码无法识别 15.xx 范围内的几个数字。

此外,我仅限于使用内置的 Python 函数。

预期输出:

([2.4, 3.44, 4.55, 4.9], [15.1, 15.31, 15.68, 15.9, 16.7, 19.3, 21])
[2.4, 3.44, 4.55, 4.9, 5.11, 5.31, 5.61, 5.99, 6.11, 7.34, 7.9, 9, 11.3]

实际输出:

([2.4, 3.44, 4.55, 4.9], [15.1, 15.68, 16.7, 21])
[2.4, 3.44, 4.55, 4.9, 5.11, 5.31, 5.61, 5.99, 6.11, 7.34, 7.9, 9, 11.3, 15.31, 15.9, 19.3]

如您所见,几个大于 15 的数字不会被添加到heavy_pkgs列表中,也不会从原始包列表中删除。

Python 数学 精度

评论

0赞 Simon Crowe 11/11/2023
之前在哪里申报?我认为我们需要您的完整代码来解决这个问题。我的猜测是,因为列表是通过引用传递的,所以在迭代它时会从中删除项目,因为两个变量都绑定到同一个列表实例。packagesdef process_packages(package_list):packages.remove(i)package_list
2赞 Simon Crowe 11/11/2023
我从您的示例中删除了输出,输出是您所期望的。请参阅此答案以了解原因:stackoverflow.com/a/1637875/9925060packages.remove(i)
0赞 Justin 11/11/2023
@SimonCrowe,不知道我们的意思。这是完整的代码。 可以在实际列表之前声明。在我看来,将函数放在主运行代码之上是一种很好的做法。def process_packages(package_list):packages

答:

3赞 ShadowRanger 11/11/2023 #1

您遇到了如何在迭代时从列表中删除项目?中的问题。

packages在全局作用域中与在函数的作用域中是相同的(它们都是相同 的别名)。当您在迭代它时从它开始迭代时,您将跳过紧跟在已删除元素之后的元素(因为它向下移动了一个槽,并且迭代器在下一个循环中前进过该槽)。package_listlistremove

用轻量级和中等权重构建一个新的,并在循环完成后替换内容,事情应该可以工作(并且工作效率更高,因为每次调用 是 ,所以整体工作是 ,而两次通过构建和替换是)。listpackage_listremoveO(n)O(n²)O(n)

def process_packages(package_list):
    light_pkgs = []
    not_heavy_pkgs = []  # Temporary list to hold stuff that should remain in package_list
    heavy_pkgs = []

    light_weight = 5.00
    heavy_weight = 15.00

    for i in package_list:
        if i < light_weight:
            light_pkgs.append(i)
            not_heavy_pkgs.append(i)  # Looks like light things are supposed to stay in too
        elif i > heavy_weight:
            heavy_pkgs.append(i)
        else:
            not_heavy_pkgs.append(i)  # It's not heavy, so retain it 

    package_list[:] = not_heavy_pkgs  # Assigning to complete slice replaces contents of
                                   # package_list in place, affecting caller's version of list
                                   # as your original code tried to do

    return light_pkgs, heavy_pkgs

该代码确实有点笨拙。虽然这将涉及对输入的更多传递,但在这里依赖一些列表推导会更简洁:

def process_packages(package_list):
    heavy_weight = 15.0
    # Get heavy packages
    heavy_pkgs = [pkg for pkg in package_list if pkg > heavy_weight]
    # Remove heavy packages from source
    package_list[:] = [pkg for pkg in package_list if pkg <= heavy_weight]

    # One more pass to get light packages and we're done
    # This pass is somewhat cheaper, because heavy items were already removed
    return [pkg for pkg in package_list if pkg < 5.0], heavy_pkgs

评论

0赞 Justin 11/11/2023
啊太棒了,谢谢。我知道这是我在 Python 如何处理删除方面所缺少的,但无法确切理解为什么循环中的项目被跳过。就我个人而言,我会使用列表推导,但我正在帮助一个朋友理解列表和循环作为基本水平,她是数学专业的。
-1赞 Suramuthu R 11/11/2023 #2

当您从 for 循环中删除一个项目时,哪个项目将成为下一个索引? 例如,如果您删除第 3 项(即 idx 3),则删除后第 4 项将成为第 3 项。(就像有人从队列中走出来一样,他旁边的人会来到他的位置)。即使在删除之后,循环也会按升序进行。所以在索引 3 之后,下一个索引是 4。但是由于之前的 index-4 现在已经变成了 index-3,现在的 index-4 就是之前的 index-5。因此,即使之前的索引 4 超过 15,也不会删除。

因此,不要使用 for 循环,而应使用 while 循环。如果删除了该项目,则将索引值减少 1,并将package_list的长度减少 1。就是这样。

def process_packages(package_list):
    light_pkgs = []
    heavy_pkgs = []
    L = len(package_list)
    i = 0

    light_weight = 5.00
    heavy_weight = 15.00

    while i<L:
        x = package_list[i]
        if x < light_weight:
            light_pkgs.append(x)
        elif x > heavy_weight:
            heavy_pkgs.append(x)
            packages.remove(x)
            i = i-1
            L = L-1
        i += 1
        
    return light_pkgs, heavy_pkgs
#Output: 
([2.4, 3.44, 4.55, 4.9], [15.1, 15.31, 15.68, 15.9, 16.7, 19.3, 21])
[2.4, 3.44, 4.55, 4.9, 5.11, 5.31, 5.61, 5.99, 6.11, 7.34, 7.9, 9, 11.3]

评论

0赞 Suramuthu R 11/19/2023
你能解释一下为什么我的答案被否决了吗?