当前位置: 首页 > 知识库问答 >
问题:

Python选择整数除法向负无穷进位的数学原因是什么?

狄楷
2023-03-14

我知道Python/向负无穷大舍入,而在C/中,它正在截断,向0舍入。

到目前为止,我知道的是:

               |remainder| 
-12 / 10  = -1,   - 2      // c++
-12 // 10 = -2,   + 8      # python

12 / -10  = -1,     2      // c++
12 // -10 = -2,   - 8      # python

12 / 10  = 1,      2       //both
12 // 10 = 1,      2 

-12 / -10 = 1,    - 2      //both
          = 2,    + 8

C++: 
1. m%(-n) == m%n
2. -m%n == -(m%n)
3. (m/n)*n + m%n == m

python:
1. m%(-n) == -8 == -(-m%n)
2. (m//n)*n + m%n == m

但是为什么Python选择向负无穷大进位呢?我没有找到任何资源来解释这一点,只是找到并听到人们含糊其辞地说:“出于数学原因”。

例如,为什么C中的-1/2计算为0,而Python中的-1

抽象地处理这些事情的人倾向于认为朝着负无穷大前进更有意义(这意味着它与数学中定义的模函数相兼容,而不是%具有某种有趣的含义)。

但是我没有看到C的/与模函数不兼容。在C语言中,(m/n)*n m%n==m也适用。

那么Python选择向负无穷舍入的(数学)原因是什么呢?

共有3个答案

松兴邦
2023-03-14

整数和实数算术都定义了它们的除法运算符,以便以下两个等价值都适用于n和d的所有值。

(n+d)/d = (n/d)+1
(-n)/d = -(n/d)

不幸的是,整数算术的定义方式不能使两者都成立。出于许多目的,第一个等价比第二个等价更有用,但在大多数情况下,如果代码将被除两个值,则将应用以下之一:

>

  • 这两个值都是正值,在这种情况下,第二个等价项是无关的。

    被除数是除数的精确整数倍,在这种情况下,两个等价项可以同时保持。

    历史上,处理包含负数的除法最简单的方法是观察是否有一个操作数为负数,去掉符号,执行除法,然后在只有一个操作数为负数的情况下使结果为负数。这将在两种常见情况下都符合要求,并且比使用在所有情况下都坚持第一等价的方法更便宜,而不仅仅是当除数是股息的精确倍数时。

    Python不应该被视为使用了低级语义,而是应该确定,即使在精确语义无关紧要的情况下,在重要的情况下通常更高级的语义也值得稍微慢一点划分。

  • 燕智
    2023-03-14

    虽然我不能提供为什么舍入模式被选择的形式定义,但是当你认为代码> %在C和Python中不是完全相同的时候,引用与<代码> %>代码>运算符兼容的引用是有意义的。

    在C语言中,它是余数运算符,而在Python中,它是模运算符——而且,当两个操作数的符号不同时,它们不一定是同一件事。对于这些运算符之间的差异,有一些很好的解释:mod和余数之间的区别是什么?

    现在,考虑到这一差异,整数除法的舍入(截断)模式必须与两种语言相同,以确保您引用的关系,(m/n)*n m%n==m,仍然有效。

    下面是两个简短的程序,它们在实际中演示了这一点(请原谅我的Python代码有些幼稚——我是该语言的初学者):

    C:

    #include <iostream>
    
    int main()
    {
        int dividend, divisor, quotient, remainder, check;
        std::cout << "Enter Dividend: ";                        // -27
        std::cin >> dividend;
        std::cout << "Enter Divisor: ";                         // 4
        std::cin >> divisor;
    
        quotient = dividend / divisor;
        std::cout << "Quotient = " << quotient << std::endl;    // -6
        remainder = dividend % divisor;
        std::cout << "Remainder = " << remainder << std::endl;  // -3
    
        check = quotient * divisor + remainder;
        std::cout << "Check = " << check << std::endl;          // -27
        return 0;
    }
    

    Python:

    print("Enter Dividend: ")             # -27
    dividend = int(input())
    print("Enter Divisor: ")              # 4
    divisor = int(input())
    quotient = dividend // divisor;
    print("Quotient = " + str(quotient))  # -7
    modulus = dividend % divisor;
    print("Remainder = " + str(modulus))  # 1
    check = quotient * divisor + modulus; # -27
    print("Check = " + str(check))
    

    请注意,对于不同符号(-27和4)的给定输入,两种语言之间的商和余数/模都不同,但恢复的check值在这两种情况下都是正确的。

    闽涵蓄
    2023-03-14

    但是为什么Python选择向负无穷大进位呢?

    我不确定最初做出这一选择的原因是否在任何地方都有记录(尽管据我所知,它可以在某个地方的一些PIP中详细解释),但我们肯定可以找到各种理由来解释它的意义。

    一个原因很简单,四舍五入为负(或正!)无穷大意味着所有数字都以相同的方式舍入,而向零舍入则使零变得特殊。数学上的说法是,向下舍入−∞是平移不变量,即它满足等式:

    round_down(x k)==round_down(x)k

    对于所有实数x和所有整数k。四舍五入到零不会,因为,例如:

    round_to_zero(0.5-1)!=round_to_zero(0.5)-1

    当然,也存在其他参数,例如您引用的参数基于与%运算符(行为)的兼容性(我们希望的方式)-下面将详细介绍。

    事实上,我想说的是,这里真正的问题是为什么Python的int()函数没有定义为将浮点参数舍入到负无穷大,这样m//n就等于int(m/n)。(我怀疑“历史原因”。)再说一遍,这也没什么大不了的,因为Python至少有数学。floor()确实满足m//n==math。楼层(m/n)

    但是我没有看到C的/与模函数不兼容。在C语言中,(m/n)*n m%n==m也适用。

    是的,但是在将/四舍五入归零的同时保留该标识需要以一种笨拙的方式为负数定义%。特别是,我们失去了Python的%的以下两个有用的数学属性:

    1. 0

    这些属性很有用,因为%的主要用途之一是将一个数字m包装到一个有限的长度范围n

    例如,假设我们正在尝试计算方向:假设航向是我们当前的罗盘航向,以度为单位(从正北顺时针计算,以0为单位)

    heading = (heading + angle) % 360
    

    这将在所有情况下简单地工作。

    然而,如果我们尝试在C中使用这个公式,它有不同的舍入规则和相应不同的%运算符,我们会发现环绕并不总是像预期的那样工作!例如,如果我们开始面向西北(航向=315)并顺时针旋转90°(角度=90),我们最终确实会面向东北(航向=45)。但如果然后尝试逆时针旋转90°(角度=-90),使用C的%操作符,我们不会像预期的那样返回航向=315,而是返回航向=-45

    为了使用C%运算符获得正确的环绕行为,我们需要将公式写成以下内容:

    heading = (heading + angle) % 360;
    if (heading < 0) heading += 360;
    

    或作为:

    heading = ((heading + angle) % 360) + 360) % 360;
    

    (更简单的公式标题=(标题角度360)%360只有在我们总是能保证标题角度的情况下才会起作用

    这是为除法使用非平移不变舍入规则,并因此使用非平移不变的%运算符所付出的代价。

     类似资料:
    • 我知道Python会趋向于负无穷大,而C会截断,趋向于0。 到目前为止,我知道的是: 但是为什么Python选择向负无穷大进位呢?我没有找到任何资源来解释这一点,只是找到并听到人们含糊其辞地说:“出于数学原因”。 例如,在为什么-1/2在C中计算为0,但在Python中为-1?中: 抽象地处理这些事情的人倾向于认为朝着负无穷大的方向移动更有意义(这意味着它与数学中定义的模函数兼容,而不是%具有某种

    • 问题内容: 给 正如它应该。然而, 给 ,但无论它是正数还是负数,我都希望它向0舍入(即,我希望-1/2为0)。最好的方法是什么? 问题答案: 进行浮点除法,然后转换为int。无需额外的模块。 Python 3: Python 2:

    • 问题内容: -1 / 5整数除法应该返回什么?我对此行为完全感到困惑。我认为数学上应该为0,但是python和ruby返回-1。 为什么不同的语言在这里表现不同?请有人解释。谢谢。 问题答案: 简短的答案: 语言设计者可以选择在进行整数除法时,其语言是四舍五入为零,负无穷大还是正无穷大。不同的语言做出了不同的选择。 长答案: Python和Ruby的语言作者都认为向负无穷大舍入比向零四舍五入更有意

    • 为什么Java中的以下代码 打印“无穷大”而不是未定义。这在数学上不是错的吗?

    • 我明白了为什么C中的-1/2计算为0,而Python中的-1?在Python中表示整数除法向无穷大舍入,即,应用于结果。 我原以为也会像那样,但实际上我得到了,这在我心目中应该是。 所以问题是:为什么和函数之间的规则不一致?有什么合理的解释吗?

    • 问题内容: 我们如何在代码中使用它们,什么会导致NaN(不是数字)? 问题答案: 如果您想了解有关Java中浮点数的更多信息,这可能是一个很好的参考。 正无穷大是一个正数,以至于无法正常表示。负无穷大是一个负数,以至于无法正常表示。NaN的意思是“不是数字”,它是由数学运算产生的,该数学运算不会产生数字,例如将0除以0。 在Java中,Double和Float类都具有代表三种情况的常量。它们是PO