web-dev-qa-db-ja.com

JavaScript%(モジュロ)は、負の数に対して負の結果を返します

Google Calculator(-13) % 6451によると。

Javascript(こちら JSBin を参照)によれば、-13です。

どうすれば修正できますか?

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

この記事から: The JavaScript Modulo Bug

227
Enrique

Number.prototypeの使用は低速です。プロトタイプメソッドを使用するたびに、番号がObjectにラップされるためです。これの代わりに:

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%高速です。もちろん、パフォーマンスがあなたにとって重要な場合。

134
StuR

JavaScriptの%演算子は剰余演算子であり、モジュロ演算子ではありません(主な違いは負数の処理方法にあります)。

-1 % 8 // -1, not 7

25
Rob Sobers

肯定的な結果を返す「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
15
Shanimal

受け入れられた答えは、%演算子を再利用するため、少し緊張します。 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
9
wisbucky

JavaScript Modulo操作

科学的な計算またはアルゴリズムの実装を成功させるには、特定の言語またはフレームワークが提供する機能を理解するだけでなく、制限を理解することも可能です。

コンピューターは正確な科学機器ですが、個別の空間でエンティティを操作することで機能します(画面上のピクセル数が限られている、各数値の背後にあるビット数が限られているなど)

制限やフレームワークの仕様を無視しようとすると、すぐに数式と作成しようとするコードの間にインピーダンスの不一致があることがわかります。

モジュロ演算子

フレームワークの関数または演算子を誤って公示したり理解したりすることにより、状況が複雑になる場合があります。この記事では、モジュロ演算子に焦点を当てます。

C#またはJavaScriptプログラマーに自分の言語のモジュロ演算子とは何かを尋ねると、答えが出される可能性が非常に高くなります:%(パーセント記号など)。多くのドキュメントでは、%記号をモジュロ演算子と呼んでいます。

うわー!これは微妙ですが非常に危険な間違いです。 C#およびJavaScriptでは、1つのオペランドが2番目のオペランドで除算されたときに残った剰余(符号付き)を計算するために%演算子が実際に使用されます。したがって、オペランドは符号付き剰余演算子として正しく参照される必要があります。

一見、符号付き剰余演算子はモジュロ演算子と同様に機能します。 JavaScriptから返された結果とGoogleから返された結果を比較して、いくつかのテストを行いましょう。

Chromeで、コンソールを開きます(F12キーを押して、[コンソール]タブを選択します)。左側の列から計算を1つずつ入力します。次に、Google検索バーに同じ表現を入力します。結果に注目してください。それらは同じでなければなりません。

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

では、最初のオペランドとして負の値を使用してみましょう。

enter image description here

驚き!

-5%3 = 1(Googleによる)-5%3 = -2(JavaScriptによる)

さて…JavaScriptで%演算子の定義を見れば、これは実際には驚くべきではありません(…またはC#または他の多くの言語)。 Googleは真のモジュロを計算しますが、これらのコンピューター言語は署名されたリマインダーを計算します。

ただし、すべてのプログラミング言語/フレームワークが%に対して同じ実装を持っているわけではありません。たとえば、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

8
Marian Veteanu

xが整数で、nが2の累乗の場合、x % nの代わりにx & (n - 1)を使用できます。

> -13 & (64 - 1)
51 
3
quasimodo

期待どおりに動作していませんが、JavaScriptが「動作」していないという意味ではありません。これは、モジュロ計算のために選択されたJavaScriptです。なぜなら、定義によりどちらの答えも理にかなっているからです。

ウィキペディアの this を参照してください。右側に、さまざまな言語が結果の記号をどのように選択したかを見ることができます。

3
dheerosaur

だから、あなたが度を変更しようとしているなら(-50度から200度を持っているなら)、あなたは次のようなものを使いたいと思うでしょう:

function modrad(m) {
    return ((((180+m) % 360) + 360) % 360)-180;
}
2
JayCrossler

負のaと負の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); 
    }
1
bormat

これはバグではなく、モジュロを計算する3つの関数があり、ニーズに合った関数を使用できます(ユークリッド関数を使用することをお勧めします)

小数部関数の切り捨て

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
1
zessx

作業を行う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

0
maartenpaauw