为什么 `0--3//2` 和 `--3//2` 有区别?
Why is there a difference between `0--3//2` and `--3//2`?
我想知道如何在没有 math
模块的情况下进行 floor/ceiling 操作。我通过使用 floor division //
解决了这个问题,发现负数“给出了上限”。所以这有效:
>>> 3//2
1
>>> -3//2
-2
我希望答案是肯定的,所以首先我尝试了 --3//2
,但这给出了 1。我推断这是因为 Python 将 --
计算为 +
.所以为了解决这个问题,我发现我可以使用 -(-3//2))
,问题解决了。
但是我想到了另一种解决方案,即(我包括前面的例子进行比较):
>>> --3//2 # Does not give ceiling
1
>>> 0--3//2 # Does give ceiling
2
我无法解释为什么包含 0 会有帮助。我已阅读有关除法的文档,但在那里没有找到任何帮助。我以为可能是评价顺序的原因:
如果我使用 --3//2
作为示例,从我的文档中可以看出 Positive, negative, bitwise NOT
在这个示例中是最严格的,我猜这会将 --
计算为 +
.接下来是 Multiplication, division, remainder
,所以我猜这是 +3//2
,计算结果为 1
,我们就完成了。我无法从文档中推断出为什么包含 0
会改变结果。
参考文献:
Python 使用符号 -
作为 unary (-x
) 和 binary (x-y
) 运算符。这些有不同的 operator precedence.
具体来说,//
的顺序是:
- 一元
-
- 二进制
//
- 二进制
-
通过将 0
作为 0--3//2
引入,第一个 -
是 binary -
并最后应用。没有前导 0
作为 --3//2
,两个 -
都是 一元 并一起应用。
对应的evaluation/syntax树大致是这样的,先评估最底层的节点,然后在父节点中使用:
---------------- ----------------
| --3//2 | 0--3//2 |
|================|================|
| | ------- |
| | | 0 - z | |
| | -----+- |
| | | |
| -------- | ----+--- |
| | x // y | | | x // y | |
| -+----+- | -+----+- |
| | | | | | |
| ----+ +-- | ---+ +-- |
| | --3 | | 2 | | | -3 | | 2 | |
| ----- --- | ---- --- |
---------------- ----------------
因为一元 -
一起应用,所以它们抵消了。相反,一元和二进制 -
分别在 之前 和 在 除法之后应用。
这是一个简单的操作顺序问题。
--3//2
等同于 (-(-3)) // 2
。由于左侧没有任何内容,因此每个 -
必须是一元否定;这比 //
具有更高的优先级;所以 3
被否定两次(产生 3)然后除以 2.
0--3//2
等同于 0 - ((-3) // 2)
。既然左边有东西,第一个-
肯定是二元减法,比//
有低的优先级。第二个-
仍然是一元否定; -3
除以 2
得到 -2
,然后从 0
中减去该值。
另一种了解 CPython 实际计算方式的方法是使用 dis 模块来查看它对堆栈机器的实际操作。
>>> import dis
>>> dis.dis('0--3//2')
1 0 LOAD_CONST 0 (2)
2 RETURN_VALUE
糟糕,常量是在编译期间计算的,所以使用一个名称。
>>> t=3
>>> dis.dis('0--t//2')
1 0 LOAD_CONST 0 (0)
2 LOAD_NAME 0 (t)
4 UNARY_NEGATIVE
6 LOAD_CONST 1 (2)
8 BINARY_FLOOR_DIVIDE
10 BINARY_SUBTRACT
12 RETURN_VALUE
我想知道如何在没有 math
模块的情况下进行 floor/ceiling 操作。我通过使用 floor division //
解决了这个问题,发现负数“给出了上限”。所以这有效:
>>> 3//2
1
>>> -3//2
-2
我希望答案是肯定的,所以首先我尝试了 --3//2
,但这给出了 1。我推断这是因为 Python 将 --
计算为 +
.所以为了解决这个问题,我发现我可以使用 -(-3//2))
,问题解决了。
但是我想到了另一种解决方案,即(我包括前面的例子进行比较):
>>> --3//2 # Does not give ceiling
1
>>> 0--3//2 # Does give ceiling
2
我无法解释为什么包含 0 会有帮助。我已阅读有关除法的文档,但在那里没有找到任何帮助。我以为可能是评价顺序的原因:
如果我使用 --3//2
作为示例,从我的文档中可以看出 Positive, negative, bitwise NOT
在这个示例中是最严格的,我猜这会将 --
计算为 +
.接下来是 Multiplication, division, remainder
,所以我猜这是 +3//2
,计算结果为 1
,我们就完成了。我无法从文档中推断出为什么包含 0
会改变结果。
参考文献:
Python 使用符号 -
作为 unary (-x
) 和 binary (x-y
) 运算符。这些有不同的 operator precedence.
具体来说,//
的顺序是:
- 一元
-
- 二进制
//
- 二进制
-
通过将 0
作为 0--3//2
引入,第一个 -
是 binary -
并最后应用。没有前导 0
作为 --3//2
,两个 -
都是 一元 并一起应用。
对应的evaluation/syntax树大致是这样的,先评估最底层的节点,然后在父节点中使用:
---------------- ----------------
| --3//2 | 0--3//2 |
|================|================|
| | ------- |
| | | 0 - z | |
| | -----+- |
| | | |
| -------- | ----+--- |
| | x // y | | | x // y | |
| -+----+- | -+----+- |
| | | | | | |
| ----+ +-- | ---+ +-- |
| | --3 | | 2 | | | -3 | | 2 | |
| ----- --- | ---- --- |
---------------- ----------------
因为一元 -
一起应用,所以它们抵消了。相反,一元和二进制 -
分别在 之前 和 在 除法之后应用。
这是一个简单的操作顺序问题。
--3//2
等同于 (-(-3)) // 2
。由于左侧没有任何内容,因此每个 -
必须是一元否定;这比 //
具有更高的优先级;所以 3
被否定两次(产生 3)然后除以 2.
0--3//2
等同于 0 - ((-3) // 2)
。既然左边有东西,第一个-
肯定是二元减法,比//
有低的优先级。第二个-
仍然是一元否定; -3
除以 2
得到 -2
,然后从 0
中减去该值。
另一种了解 CPython 实际计算方式的方法是使用 dis 模块来查看它对堆栈机器的实际操作。
>>> import dis
>>> dis.dis('0--3//2')
1 0 LOAD_CONST 0 (2)
2 RETURN_VALUE
糟糕,常量是在编译期间计算的,所以使用一个名称。
>>> t=3
>>> dis.dis('0--t//2')
1 0 LOAD_CONST 0 (0)
2 LOAD_NAME 0 (t)
4 UNARY_NEGATIVE
6 LOAD_CONST 1 (2)
8 BINARY_FLOOR_DIVIDE
10 BINARY_SUBTRACT
12 RETURN_VALUE