【问题标题】:Result of -1%7 is different in javascript(-1) and python(6)-1%7 的结果在 javascript(-1) 和 python(6) 中不同
【发布时间】:2021-10-23 19:24:26
【问题描述】:

JavaScript 中的表达式-1 % 7 给出了-1 作为结果。而在 Python 和 Haskell 中,我发现结果是 6

谁能解释为什么两者有不同的行为?哪一个是正确的?

【问题讨论】:

标签: javascript python haskell


【解决方案1】:

两者都是正确的,它们只是在处理负操作数方面使用了不同的约定。对于正数,约定一致,但对于负数,约定不一致。在 Python 中,a % b 始终与b 具有相同的符号。

接下来,我将使用 Python 表示法,其中// 用于整数除法。

q, r  = a // b, a % b

然后

a == q * b + r

必须在任何语言中为真(假设ab 是整数,b 不等于零)。所以余数的处理方式必须与整数除法的约定一致。在 Python 中,整数除法是地板除法,即,结果向负无穷大舍入。在其他一些语言中,改为使用向零舍入。在某些语言中,您会得到 CPU 制造商决定实施的任何约定,因此在不同硬件上运行相同的代码会产生不同的结果。您可以想象,这可能有点烦人。 :)

【讨论】:

    【解决方案2】:

    % 代表 JavaScript 和 Python 中的不同运算符。

    在 JavaScript 中,% 代表 Remainder 运算符。文档已经指出了余数和模运算之间的区别:

    余数运算符返回一个操作数除以第二个操作数时的余数。它总是取除数的符号,而不是除数。它使用内置的模函数来产生结果,即 var1 除以 var2 的整数余数——例如——var1 modulo var2。有一个建议是在未来版本的 ECMAScript 中获得一个实际的模运算符,不同之处在于模运算符的结果将采用除数的符号,而不是被除数。

    (我强调)

    与此相反:在 Python 中,% 代表 modulo 运算符。该文档还对标志进行了说明:

    %(模)运算符从第一个参数除以第二个参数产生余数。数字参数首先转换为通用类型。零右参数引发 ZeroDivisionError 异常。 [...] 模运算符总是产生与第二个操作数(或零)具有相同符号的结果;结果的绝对值严格小于第二个操作数[2]的绝对值。

    (我强调)

    【讨论】:

      【解决方案3】:

      两者都是正确的。一些语言返回正模数,而另一些语言则保留它们的符号。

      您可以简单地将模数添加到变量中以获得正数,或者在执行模数运算之前检查数字是正数还是负数,然后在两者之间切换后更正结果。


      在两者之间转换a%b的伪代码:

      -1%7 == -1 的语言中,你这样做是为了得到一个正数:

      ((a%b)+b) % b
      

      -1%7 == 6 的语言中,您可以这样做以获得签名版本:

      if a < 0:
        return (a%b)-b
      else:
        return a%b
      

      【讨论】:

      • 这些代码 sn-ps 都不正确...第一个应该是 ((a%b)+b)%b(或者 if a &lt; 0: return (a%b)+b else: return a%b,如果您更喜欢条件而不是模数),第二个应该是 if a &lt; 0: return (a%b)-b else: return a%b
      • @DanielWagner 很好,我已经习惯了不用担心溢出。第二个被颠倒了。
      【解决方案4】:

      我将给出一个稍微不同的答案。正如其他人所说,函数可以做任何你定义它们的事情,m - x = -x mod m。作为前奏,我会注意到 Haskell 有两个“mod”函数,modrem,它们仅在这方面有所不同。您可以证明mod 在数学上更可取。 rem 对应于您在 x86 处理器上获得的内容。事实上,还有第三种,欧几里得的,它可能更好,正如 Raymond Boute 在The Euclidean Definitions of the Functions Div and Mod 中所描述的那样。第三种形式 always 返回一个正模数。 (事实上​​,至少还有另外两种选择。)

      所以,Javascript 的定义是你从大多数机器mod 操作码中得到的。从这个意义上说,它可能更可取,因为这将使其更有效地实施。在数学上,Haskell 和 Python 的定义比 Javascript 的要好。还有第三种定义可能会更好一些。

      Euclidean 和 Haskell/Python 定义都拥有的一个关键属性是 x mod m = y mod m 相当于 x = y mod m Javascript 的定义所缺少的。您可以通过在Javascript中计算6 % 7来验证。

      【讨论】:

        【解决方案5】:

        两者都是正确的。

        要完成其他答案,您还可以考虑 Python 中的divmod 函数:

        使用整数除法时,将两个(非复数)数字作为参数并返回一对由它们的商和余数组成的数字。对于混合操作数类型,适用二元算术运算符的规则。对于整数,结果与(a // b, a % b) 相同。对于浮点数,结果为(q, a % b),其中q 通常为math.floor(a / b),但可能比它小1。在任何情况下,q * b + a % b 非常接近 a,如果 a % b 不为零,则它与 b0 &lt;= abs(a % b) &lt; abs(b) 具有相同的符号。

        >>> divmod(-1, 7)
        (-1, 6)
        

        【讨论】:

        • 感谢您提到 Python 允许 //% 使用浮点操作数。 FWIW,a // b, a % b 可能比divmod(a, b) 更快,因为调用函数与使用运算符相比会产生额外开销,尽管divmod 并不像调用用 Python 编写的函数那么慢。
        • @PM2Ring 不是 Python 中的运算符只是具有替代语法的函数吗?我不认为任何具有良好优化的现代语言在使用函数调用时都会不可避免地降低性能,即使是在内置运算符上——如果函数足够简单,无论如何它都会被内联。
        • @leftaroundabout 否,因为函数调用会调用一个新的 frame 对象,以便函数的代码可以在其自己的范围内运行。框架是一个成熟的 Python 对象,因此这样做的开销比调用 C 函数要多一些。 Python 的优化器相当简单,它不会内联函数调用。前面的所有评论都适用于标准 CPython,其他实现的细节可能会有所不同。
        • @leftaroundabout 不幸的是,CPython 并非如此。此外,Python 语言语义使得内联函数调用变得非常困难。
        猜你喜欢
        • 2018-05-29
        • 1970-01-01
        • 2016-04-11
        • 1970-01-01
        • 1970-01-01
        • 2020-12-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多