JavaScript% (modulo) cho kết quả âm cho số âm


252

Theo Google Máy tính (-13) % 6451.

Theo Javascript (xem JSBin này ) nó là -13.

Làm thế nào để tôi sửa lỗi này?


Đây có thể chỉ là một vấn đề ưu tiên. Bạn có nghĩa là (-13) % 64hay -(13 % 64)? Cá nhân, tôi đã đặt parens bằng mọi cách, chỉ để thêm rõ ràng.
MatrixFrog

2
về cơ bản là một bản sao của java làm các phép tính mô đun với các số âm như thế nào? mặc dù đây là một câu hỏi javascript.
Tổng thống James K. Polk

85
Javascript đôi khi cảm thấy như một trò đùa rất tàn nhẫn
dukeofgaming

6
google không thể sai
caub

10
Vấn đề cơ bản là trong JS %không phải là toán tử modulo. Đó là toán tử còn lại. Không có toán tử modulo trong JavaScript. Vì vậy, câu trả lời được chấp nhận là con đường để đi.
Redu

Câu trả lời:


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

Lấy từ bài viết này: Lỗi Modulo JavaScript


23
Tôi không biết rằng tôi sẽ gọi nó là "lỗi". Hoạt động modulo không được xác định rõ ràng qua các số âm và các môi trường điện toán khác nhau xử lý nó khác nhau. Bài viết của Wikipedia về hoạt động modulo bao gồm nó khá tốt.
Daniel Pryden

22
Nó có vẻ ngớ ngẩn vì nó thường được gọi là 'modulo', cho thấy nó sẽ hành xử giống như định nghĩa toán học của nó (xem đại số ℤ / nℤ), điều này không có.
etienne

7
Tại sao phải dùng modulo trước khi thêm n? Tại sao không chỉ thêm n và sau đó lấy modulo?
bắt đầu từ

12
@starwed nếu bạn không sử dụng% n này thì nó sẽ thất bại x < -n- ví dụ như (-7 + 5) % 5 === -2nhưng ((-7 % 5) + 5) % 5 == 3.
fadedbee

7
Tôi khuyên bạn nên thêm vào câu trả lời rằng để truy cập chức năng này, người ta nên sử dụng định dạng (-13) .mod (10) thay vì -13% 10. Nó sẽ rõ ràng hơn.
Jp_

161

Sử dụng Number.prototypelà SLOW, vì mỗi lần bạn sử dụng phương thức nguyên mẫu, số của bạn được gói trong một Object. Thay vì điều này:

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

Sử dụng:

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

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

~ 97% nhanh hơn so với sử dụng nguyên mẫu. Nếu hiệu suất là quan trọng đối với bạn tất nhiên ..


1
Mẹo tuyệt vời. Tôi đã lấy jsperf của bạn và so sánh với các giải pháp còn lại trong câu hỏi này (nhưng có vẻ như đây là cách tốt nhất): jsperf.com/negative-modulo/3
Mariano Desanze

11
Tối ưu hóa vi mô. Bạn sẽ phải thực hiện một số lượng lớn các phép tính mod cho việc này để tạo ra bất kỳ sự khác biệt nào. Mã những gì rõ ràng nhất và dễ bảo trì nhất, sau đó tối ưu hóa sau phân tích hiệu suất.
ChrisV

Tôi nghĩ rằng bạn đã có bạn ns và ms xung quanh một cách sai lầm trong ví dụ @StuR thứ hai của bạn. Nó phải return ((n % m) + m) % m;.
vimist

Đây phải là một nhận xét cho câu trả lời được chấp nhận, không phải là một câu trả lời cho chính nó.
xehpuk

5
Động lực nêu trong câu trả lời này là tối ưu hóa vi mô, vâng, nhưng sửa đổi nguyên mẫu là vấn đề. Thích cách tiếp cận với ít tác dụng phụ nhất, đó là cách này.
Keen

30

Các %nhà điều hành trong JavaScript là các nhà điều hành còn lại, không phải là toán tử modulo (sự khác biệt chính là ở cách số âm được điều trị):

-1 % 8 // -1, not 7


8
nên được gọi là toán tử còn lại nhưng nó được gọi là toán tử modulus: developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/
tựa

15
@DaveKennedy: MDN không phải là tài liệu tham khảo ngôn ngữ chính thức, đây là trang web được chỉnh sửa cộng đồng đôi khi bị lỗi. Thông số kỹ thuật không gọi nó là toán tử modulo, và theo như tôi có thể nói nó không bao giờ có (tôi đã quay lại ES3). Nó nói rõ ràng rằng toán tử mang lại phần còn lại của một bộ phận ngụ ý và chỉ gọi nó là "toán tử%".
TJ Crowder

2
Nếu nó được gọi remainder, nó phải lớn hơn 0 theo định nghĩa. Bạn không thể nhớ định lý phân chia từ trường trung học?! Vì vậy, có lẽ bạn có thể có một cái nhìn ở đây: en.wikipedia.org/wiki/Euclidean_division
Ahmad

19

Hàm "mod" để trả về kết quả dương.

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

Và tất nhiên

mod(-13,64) // 51

1
MDN không phải là tài liệu tham khảo ngôn ngữ chính thức, đây là trang web được chỉnh sửa cộng đồng đôi khi bị sai. Thông số kỹ thuật không gọi nó là toán tử modulo, và theo như tôi có thể nói nó không bao giờ có (tôi đã quay lại ES3). Nó nói rõ ràng rằng toán tử mang lại phần còn lại của một bộ phận ngụ ý và chỉ gọi nó là "toán tử%".
TJ Crowder

1
Rất tiếc, liên kết bạn đã chỉ định thực sự tham chiếu #sec-applying-the-mod-operatorngay trong url :) Dù sao, cảm ơn bạn đã ghi chú, tôi đã lấy ra câu trả lời, dù sao nó cũng không thực sự quan trọng.
Shanimal

3
@ Shanimal: LOL! Nó làm. Một lỗi do trình soạn thảo HTML. Các văn bản spec không.
TJ Crowder

10

Câu trả lời được chấp nhận làm tôi hơi lo lắng vì nó sử dụng lại toán tử%. Điều gì xảy ra nếu Javascript thay đổi hành vi trong tương lai?

Đây là một cách giải quyết không sử dụng lại%:

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

8
Nếu javascript thay đổi toán tử modulo để phù hợp với định nghĩa toán học, câu trả lời được chấp nhận vẫn sẽ hoạt động.
bắt đầu từ

20
"Điều gì sẽ xảy ra nếu Javascript thay đổi hành vi trong tương lai?" - Tại sao nó? Thay đổi hành vi của một nhà điều hành cơ bản như vậy là không thể.
nnnnnn

1
+1 để chia sẻ mối quan tâm này - về & thay thế - cho câu trả lời nổi bật # answer-4467559 & vì 4 lý do: (1) Tại sao lại nêu, và vâng Thay đổi hành vi của một op cơ bản như vậy không có khả năng nhưng vẫn thận trọng khi xem xét thậm chí để tìm thấy nó không cần thiết. . cái nhìn lướt qua. (4) tiny: nó sử dụng 1 div + 1 mul thay vì 2 (mod) div & tôi đã nghe trên MUCH phần cứng trước đó với FPU tốt, nhân nhanh hơn.
Destiny Architect

2
@DestinyArchitect nó không thận trọng, nó là vô nghĩa. Nếu họ thay đổi hành vi của toán tử còn lại, nó sẽ phá vỡ một loạt các chương trình sử dụng nó. Điều đó sẽ không bao giờ xảy ra.
Aegis

10
Điều gì nếu hành vi của -, *, /, ;, ., (, ), ,, Math.floor, functionhoặc returnthay đổi? Sau đó, mã của bạn bị phá vỡ khủng khiếp.
xehpuk

5

Mặc dù nó không hoạt động như bạn mong đợi, nhưng điều đó không có nghĩa là JavaScript không 'hành xử'. Nó là một JavaScript lựa chọn được thực hiện để tính toán modulo. Bởi vì, theo định nghĩa hoặc câu trả lời có ý nghĩa.

Xem điều này từ Wikipedia. Bạn có thể thấy bên phải cách các ngôn ngữ khác nhau đã chọn dấu hiệu của kết quả.


4

Nếu xlà số nguyên và ncó lũy thừa bằng 2, bạn có thể sử dụng x & (n - 1)thay cho x % n.

> -13 & (64 - 1)
51 

2

Vì vậy, có vẻ như nếu bạn đang cố gắng điều chỉnh xung quanh độ (để nếu bạn có -50 độ - 200 độ), bạn sẽ muốn sử dụng một cái gì đó như:

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

1

Tôi cũng đối phó với n và a âm

 //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

Đây không phải là một lỗi, có 3 hàm để tính modulo, bạn có thể sử dụng hàm phù hợp với nhu cầu của mình (tôi khuyên bạn nên sử dụng hàm Euclide)

Cắt bớt phần thập phân

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

Hàm số nguyên

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

Chức năng Euclide

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
Trong hàm euclidian, kiểm tra m <0 là vô ích vì ((% n) + n)% n này luôn dương
bormat

1
@bormat Đúng vậy, nhưng trong Javascript %có thể trả về kết quả âm tính (đây là mục đích của các chức năng này, để khắc phục nó)
zessx

bạn đã viết [code] Number.prototype.mod = function (n) {var m = ((this% n) + n)% n; trả về m <0? m + Math.abs (n): m; }; [/ code] cho tôi một giá trị của n trong đó m là sai số. chúng không có giá trị của n trong đó m là sai số vì bạn thêm n sau% đầu tiên.
bormat

Nếu không có kiểm tra này, parseInt(-41).mod(-7)sẽ quay lại -6thay vì 1(và đây chính xác là mục đích của chức năng phần Integer tôi đã viết)
zessx

1
Bạn có thể đơn giản hóa chức năng của mình bằng cách xóa modulo Number.prototype.mod = function (n) {var m = this% n; trả lại (m <0)? m + Math.abs (n): m; };
bormat

0

Có một gói NPM sẽ làm việc cho bạn. Bạn có thể cài đặt nó bằng lệnh sau.

npm install just-modulo --save

Cách sử dụng được sao chép từ 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

Kho GitHub có thể được tìm thấy thông qua liên kết sau:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.