首页 文章

JavaScript%(modulo)给出负数的否定结果

提问于
浏览
199

根据Google Calculator (-13) % 6451 .

根据Javascript(请参阅此JSBin),它是 -13 .

我该如何解决?

12 回答

  • 3

    所以看起来如果你试图修改度(如果你有-50度 - 200度),你会想要使用类似的东西:

    function modrad(m) {
        return ((((180+m) % 360) + 360) % 360)-180;
    }
    
  • 8
    Number.prototype.mod = function(n) {
        return ((this%n)+n)%n;
    };
    

    取自本文:The JavaScript Modulo Bug

  • 2

    使用 Number.prototype 是缓慢的,因为每次使用原型方法时,您的数字都包含在_1701141中 . 而不是这个:

    Number.prototype.mod = function(n) {
      return ((this % n) + n) % n;
    }
    

    使用:

    function mod(n, m) {
      return ((n % m) + m) % m;
    }
    

    见:http://jsperf.com/negative-modulo/2

    比原型快〜97% . 如果表现当然对你很重要..

  • 23

    JavaScript中的 % 运算符是余数运算符,而不是模运算符(主要区别在于如何处理负数):

    -1 % 8 // -1, not 7

  • 115

    用于返回正结果的“mod”函数 .

    var mod = function (n, m) {
        var remain = n % m;
        return Math.floor(remain >= 0 ? remain : remain + m);
    };
    mod(5,22)   // 5
    mod(25,22)  // 3
    mod(-1,22)  // 21
    mod(-2,22)  // 20
    mod(0,22)   // 0
    mod(-1,22)  // 21
    mod(-21,22) // 1
    

    而且当然

    mod(-13,64) // 51
    
  • 3

    接受的答案让我有点紧张,因为它重新使用了%运算符 . 如果Javascript将来改变行为怎么办?

    这是一个不会重复使用%的解决方法:

    function mod(a, n) {
        return a - (n * Math.floor(a/n));
    }
    
    mod(1,64); // 1
    mod(63,64); // 63
    mod(64,64); // 0
    mod(65,64); // 1
    mod(0,64); // 0
    mod(-1,64); // 63
    mod(-13,64); // 51
    mod(-63,64); // 1
    mod(-64,64); // 0
    mod(-65,64); // 63
    
  • 1

    JavaScript模运算

    成功实施科学计算或算法不仅可以通过了解特定语言或框架提供的功能,还可以了解其局限性 .

    计算机是精确的科学仪器,但它们通过操纵离散空间中的实体来工作(屏幕上的像素数量有限,每个数字后面的位数有限,等等)

    尽量忽略限制或框架规范,很快你就会发现你的数学公式与你尝试编写的代码之间存在阻抗不匹配 .

    模运算符

    有时,错误地宣传或理解框架功能或操作员会使情况变得复杂 . 本文重点介绍模运算符 .

    询问任何C#或JavaScript程序员他们的语言中的模运算符是什么,他们很可能会回答:%(例如百分号) . 大量文档将%符号称为模运算符 .

    哇!这是一个微妙但非常危险的错误 . 在C#和JavaScript中,%运算符实际用于计算当一个操作数除以第二个操作数时剩余的余数(带符号) . 因此,操作数应正确地称为有符号余数运算符 .

    乍一看,带符号的余数运算符的功能与模运算符类似 . 让我们通过将JavaScript返回的结果与Google返回的结果进行比较来进行一些测试 .

    在Chrome中,打开控制台(按F12并选择控制台选项卡) . 从左列开始逐个输入 . 接下来在Google搜索栏中输入相同的表达式 . 注意结果 . 它们应该是一样的 .

    JavaScript  Google
        5 % 3       2           2
        26 % 26     0           0
        15 % 12     3           3
    

    我们现在尝试使用负值作为第一个操作数:

    enter image description here

    惊喜!

    -5%3 = 1(根据谷歌)-5%3 = -2(根据JavaScript)

    嗯......如果我们看一下JavaScript中的%运算符的定义(......甚至C#或许多其他语言),这实际上不应该是一个惊喜 . 谷歌计算真正的模数,而这些计算机语言计算签名提醒 .

    但是,并非所有编程语言/框架都具有相同的%实现 . 例如,在Python中,%运算符以与Google相同的方式计算真模数 .

    enter image description here

    语言之间的这种行为差异可能会在计算中引入细微的错误,尤其是当您尝试将算法从一种语言移植到另一种语言时!

    理解的问题是问题解决了一半

    假设我们需要使用模运算在JavaScript中实现(科学)计算 .

    由于我们现在明白JavaScript没有真正的模运算符,因此我们可以轻松地将模运算作为函数来实现 .

    有多种方法可以在JavaScript中实现模数 . 我将向您展示3种方法 .

    // Implement modulo by replacing the negative operand 
    // with an equivalent positive operand that has the same wrap-around effect
    function mod(n, p)
    {
        if ( n < 0 )
            n = p - Math.abs(n) % p;
    
        return n % p;
    }
    
    // Implement modulo by relying on the fact that the negative remainder
    // is always p numbers away from a positive reminder
    // Ex: -5 % 3 | -5 = -2 * 3 + 1 and -5 = -1 * 3 + (-2) | -2 + 3 = 1  
    function mod(n, p)
    {
        var r = n % p;
    
        return r < 0 ? r + p : r;
    }
    
    // Implement modulo by solving n = v * p + r equation  
    function mod(n, p) 
    {
        return n - p * Math.floor( n / p );
    }
    

    借助我们可以使用的更精确的工具,我们现在已准备好处理该(科学)计算并期望每次都能获得正确的结果 .

    注意:有很多计算使用模运算...如果你想看看如何在实现Caesar Cipher / ROT13代码中使用这些新的模数函数,你可以检查这个article .

  • 0

    虽然它的行为并不像您预期的那样,但这并不意味着JavaScript不会“表现” . 这是一个选择JavaScript为其模数计算 . 因为,根据定义,答案都是有道理的 .

    请参阅维基百科的this . 您可以在右侧看到不同语言如何选择结果的符号 .

  • 1

    如果 x 是整数且 n 是2的幂,则可以使用 x & (n - 1) 而不是 x % n .

    > -13 & (64 - 1)
    51
    
  • 214

    我也讨论了n和负n

    //best perf, hard to read
       function modul3(a,n){
            r = a/n | 0 ;
            if(a < 0){ 
                r += n < 0 ? 1 : -1
            }
            return a - n * r 
        }
        // shorter code
        function modul(a,n){
            return  a%n + (a < 0 && Math.abs(n)); 
        }
    
        //beetween perf and small code
        function modul(a,n){
            return a - n * Math[n > 0 ? 'floor' : 'ceil'](a/n); 
        }
    
  • 14

    这不是一个bug,有三个函数来计算模数,你可以使用一个符合你需要的函数(我建议使用欧几里得函数)

    截断小数部分功能

    console.log(  41 %  7 ); //  6
    console.log( -41 %  7 ); // -6
    console.log( -41 % -7 ); // -6
    console.log(  41 % -7 ); //  6
    

    整数部分功能

    Number.prototype.mod = function(n) {
        return ((this%n)+n)%n;
    };
    
    console.log( parseInt( 41).mod( 7) ); //  6
    console.log( parseInt(-41).mod( 7) ); //  1
    console.log( parseInt(-41).mod(-7) ); // -6
    console.log( parseInt( 41).mod(-7) ); // -1
    

    欧几里德函数

    Number.prototype.mod = function(n) {
        var m = ((this%n)+n)%n;
        return m < 0 ? m + Math.abs(n) : m;
    };
    
    console.log( parseInt( 41).mod( 7) ); // 6
    console.log( parseInt(-41).mod( 7) ); // 1
    console.log( parseInt(-41).mod(-7) ); // 1
    console.log( parseInt( 41).mod(-7) ); // 6
    
  • 6

    有一个NPM包可以为您完成工作 . 您可以使用以下命令安装它 .

    npm install just-modulo --save

    用法从README复制

    import modulo from 'just-modulo';
    
    modulo(7, 5); // 2
    modulo(17, 23); // 17
    modulo(16.2, 3.8); // 17
    modulo(5.8, 3.4); //2.4
    modulo(4, 0); // 4
    modulo(-7, 5); // 3
    modulo(-2, 15); // 13
    modulo(-5.8, 3.4); // 1
    modulo(12, -1); // NaN
    modulo(-3, -8); // NaN
    modulo(12, 'apple'); // NaN
    modulo('bee', 9); // NaN
    modulo(null, undefined); // NaN
    

    可以通过以下链接找到GitHub存储库:

    https://github.com/angus-c/just/tree/master/packages/number-modulo

相关问题