JavaScript% (modulo) ให้ผลลบสำหรับตัวเลขลบ


252

ตามที่Google เครื่องคิดเลข เป็น(-13) % 6451

ตาม Javascript (เห็นนี้JSBin ) -13มันเป็น

ฉันจะแก้ไขสิ่งนี้ได้อย่างไร


นี่อาจเป็นปัญหาที่มาก่อน คุณหมายถึง(-13) % 64หรือ-(13 % 64)? โดยส่วนตัวแล้วฉันจะใส่ parens ด้วยวิธีใดวิธีหนึ่งเพื่อความชัดเจนเป็นพิเศษ
MatrixFrog

2
โดยพื้นฐานแล้วซ้ำซ้อนกับJava ทำการคำนวณโมดูลัสด้วยจำนวนลบอย่างไร แม้ว่านี่จะเป็นคำถามจาวาสคริปต์
ประธานาธิบดี James K. Polk

85
บางครั้ง Javascript รู้สึกเหมือนเป็นเรื่องตลกที่โหดร้ายมาก
dukeofgaming

6
Google ไม่สามารถจะผิด
caub

10
ปัญหาพื้นฐานอยู่ใน JS %ไม่ใช่ตัวดำเนินการโมดูโล มันเป็นผู้ประกอบการที่เหลือ ไม่มีโอเปอเรเตอร์แบบโมดูโลใน JavaScript ดังนั้นคำตอบที่ได้รับการยอมรับเป็นวิธีที่จะไป
Redu

คำตอบ:


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

นำมาจากบทความนี้: JavaScript Modulo Bug


23
ฉันไม่รู้ว่าฉันจะเรียกมันว่า "บั๊ก" การดำเนินการแบบโมดูโลนั้นไม่ได้นิยามไว้อย่างชัดเจนเหนือจำนวนลบ บทความของ Wikipedia เกี่ยวกับการทำงานของ moduloนั้นครอบคลุมค่อนข้างดี
Daniel Pryden

22
มันอาจดูโง่เง่าเพราะมันมักเรียกว่า 'modulo' ซึ่งแนะนำว่ามันจะทำงานเหมือนกับนิยามทางคณิตศาสตร์ของมัน (ดูℤ / nℤพีชคณิต) ซึ่งไม่มี
etienne

7
ทำไมต้องใช้โมดูโล่ก่อนเพิ่ม n ทำไมไม่เพียงแค่เพิ่ม n แล้วนำโมดูโล่ไปใช้?
ดาวเมื่อ

12
@starwed ถ้าคุณไม่ได้ใช้% n นี้มันจะล้มเหลวx < -n- เช่นแต่(-7 + 5) % 5 === -2 ((-7 % 5) + 5) % 5 == 3
fadedbee

7
ฉันขอแนะนำให้เพิ่มคำตอบที่จะเข้าถึงฟังก์ชั่นนี้หนึ่งควรใช้รูปแบบ (-13) .mod (10) แทน -13% 10 มันจะชัดเจนมากขึ้น
Jp_

161

ใช้ช้าเพราะทุกครั้งที่คุณใช้วิธีการต้นแบบหมายเลขของคุณถูกห่อใน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% ~ โดยใช้ต้นแบบ ถ้าการแสดงมีความสำคัญต่อคุณแน่นอน


1
สุดยอดเคล็ดลับ ฉันใช้ jsperf ของคุณและเปรียบเทียบกับโซลูชันที่เหลือในคำถามนี้ (แต่ดูเหมือนว่านี่จะดีที่สุดแล้ว): jsperf.com/negative-modulo/3
Mariano Desanze

11
Micro-การเพิ่มประสิทธิภาพ คุณควรจะต้องมีการทำขนาดใหญ่จำนวนเงินของการคำนวณพอควรสำหรับการนี้เพื่อให้การใด ๆ ที่แตกต่างใด ๆ โค้ดที่ชัดเจนและสามารถดูแลรักษาได้มากที่สุดจากนั้นปรับการวิเคราะห์ประสิทธิภาพให้เหมาะสม
ChrisV

ฉันคิดว่าคุณมีของคุณnและms รอบทางที่ผิดในตัวอย่างที่สองของคุณ @StuR return ((n % m) + m) % m;มันควรจะเป็น
เขียนการ์ตูน

นี่ควรเป็นข้อคิดเห็นสำหรับคำตอบที่ยอมรับไม่ใช่คำตอบสำหรับตัวเอง
xehpuk

5
แรงจูงใจที่ระบุไว้ในคำตอบนี้เป็นการเพิ่มประสิทธิภาพแบบไมโครใช่ แต่การดัดแปลงต้นแบบนั้นเป็นปัญหา ชอบแนวทางที่มีผลข้างเคียงน้อยที่สุดซึ่งเป็นสิ่งนี้
คม

30

%ผู้ประกอบการใน JavaScript เป็นผู้ประกอบการที่เหลือไม่ได้ผู้ประกอบการโมดูโล (ที่แตกต่างที่สำคัญอยู่ในวิธีการที่ตัวเลขติดลบได้รับการรักษา):

-1 % 8 // -1, not 7


8
มันควรจะเรียกว่าผู้ประกอบการส่วนที่เหลือ แต่มันจะถูกเรียกว่าผู้ประกอบการโมดูลัส: developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/...
บิ๊ก McLargeHuge

15
@DaveKennedy: MDN ไม่ได้อ้างอิงภาษาอย่างเป็นทางการมันเป็นเว็บไซต์ที่มีการแก้ไขโดยชุมชนซึ่งบางครั้งมันผิด สเป็คไม่ได้เรียกมันว่าโอเปอเรเตอร์โมดูโล่และเท่าที่ฉันสามารถบอกได้ว่ามันไม่เคยมี (ฉันกลับไปที่ ES3) มันบอกอย่างชัดเจนว่าผู้ประกอบการให้ผลตอบแทนส่วนที่เหลือของการแบ่งโดยนัยและเพียงแค่เรียกมันว่า "การดำเนินการ%
TJ Crowder

2
ถ้ามันถูกเรียกremainderมันจะต้องมีขนาดใหญ่กว่า 0 โดยนิยาม คุณจำทฤษฎีบทการแบ่งแยกจากโรงเรียนมัธยมไม่ได้หรือ! ดังนั้นบางทีคุณสามารถดูได้ที่นี่: en.wikipedia.org/wiki/Euclidean_division
Ahmad

19

ฟังก์ชัน "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

1
MDN ไม่ได้อ้างอิงภาษาอย่างเป็นทางการมันเป็นเว็บไซต์ที่แก้ไขโดยชุมชนซึ่งบางครั้งมันผิด สเป็คไม่ได้เรียกมันว่าโอเปอเรเตอร์โมดูโล่และเท่าที่ฉันสามารถบอกได้ว่ามันไม่เคยมี (ฉันกลับไปที่ ES3) มันบอกอย่างชัดเจนว่าผู้ประกอบการให้ผลตอบแทนส่วนที่เหลือของการแบ่งโดยนัยและเพียงแค่เรียกมันว่า "การดำเนินการ%
TJ Crowder

1
โอ๊ะลิงค์ที่คุณระบุมีการอ้างอิงจริง#sec-applying-the-mod-operatorใน url :) ยังไงก็ตามขอบคุณสำหรับข้อความฉันเอาคำตอบออกมามันไม่สำคัญเลย
Shanimal

3
@ Shanimal: ฮ่า ๆ ! มันทำ ข้อผิดพลาดโดยตัวแก้ไข HTML ข้อความสเป็คไม่ได้
TJ Crowder

10

คำตอบที่ยอมรับทำให้ฉันกังวลเล็กน้อยเพราะใช้ตัวดำเนินการ% อีกครั้ง จะเกิดอะไรขึ้นถ้า 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

8
หากจาวาสคริปต์เปลี่ยนโมเดอเรเตอร์ของโมดูโล่ให้ตรงกับคำจำกัดความทางคณิตศาสตร์คำตอบที่ยอมรับจะยังคงใช้ได้
ดาวเมื่อ

20
"จะเกิดอะไรขึ้นถ้า Javascript เปลี่ยนพฤติกรรมในอนาคต" - ทำไมล่ะ การเปลี่ยนพฤติกรรมของผู้ให้บริการพื้นฐานไม่น่าจะเป็นไปได้
nnnnnn

1
+1 สำหรับการแบ่งปันความกังวล - ของ & ทางเลือก - กับคำตอบที่โดดเด่น # answer-4467559 & สำหรับ4เหตุผล: (1) เหตุใดจึงระบุ & ใช่“ การเปลี่ยนพฤติกรรมของ op พื้นฐานนั้นไม่น่าเป็นไปได้” แต่ยังคงต้องพิจารณาอย่างรอบคอบ แม้จะพบว่ามันไม่จำเป็น (2) กำหนด op ทำงานในแง่ของคนที่ขาดในขณะที่น่าประทับใจเป็นกังวลอย่างน้อยในลักษณะที่ 1 ที่ควรจะไม่แสดง (3) tho ฉัน hvnt ตรวจสอบทางเลือกนี้ฉันพบว่าง่ายต่อการปฏิบัติตาม ดูอย่างรวดเร็ว (4) จิ๋ว: มันใช้ 1 Div + 1 mul แทน 2 (mod) divs & ฉันเคยได้ยินฮาร์ดแวร์รุ่นก่อน ๆ ที่มี FPU ที่ดีมาก ๆ การเพิ่มทวีคูณเร็วขึ้น
Destiny Architect

2
@DestinyArchitect มันไม่รอบคอบมันไร้จุดหมาย หากพวกเขาต้องเปลี่ยนพฤติกรรมของผู้ปฏิบัติงานส่วนที่เหลือมันก็จะแตกช่วงของโปรแกรมที่ใช้งาน นั่นจะไม่เกิดขึ้น
Aegis

10
เกิดอะไรขึ้นถ้าพฤติกรรมของ-, *, /, ;, ., (, ), ,, Math.floor, functionหรือreturnการเปลี่ยนแปลงหรือไม่ จากนั้นรหัสของคุณเสียอย่างรุนแรง
xehpuk

5

แม้ว่ามันจะไม่ทำงานอย่างที่คุณคาดไว้ แต่ก็ไม่ได้หมายความว่า JavaScript ไม่ได้ทำงาน มันเป็นตัวเลือกจาวาสคริปต์ที่ทำขึ้นสำหรับการคำนวณแบบโมดูโล เพราะตามนิยามแล้วคำตอบก็สมเหตุสมผลแล้ว

ดูสิ่งนี้จาก Wikipedia คุณสามารถดูได้อย่างถูกต้องว่าภาษาต่าง ๆ เลือกสัญญาณของผลลัพธ์อย่างไร


3

ถ้าxเป็นจำนวนเต็มและnเป็นอำนาจของ 2, คุณสามารถใช้แทนx & (n - 1)x % n

> -13 & (64 - 1)
51 

2

ดังนั้นดูเหมือนว่าหากคุณพยายามปรับเปลี่ยนองศา (ดังนั้นถ้าคุณมี -50 องศา - 200 องศา) คุณต้องใช้สิ่งต่อไปนี้:

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

1

ฉันจัดการกับnégative 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

นี่ไม่ใช่ข้อผิดพลาดมี 3 ฟังก์ชั่นในการคำนวณ modulo คุณสามารถใช้สิ่งที่เหมาะสมกับความต้องการของคุณ (ฉันอยากจะแนะนำให้ใช้ฟังก์ชัน Euclidean)

ตัดทอนฟังก์ชันส่วนทศนิยม

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
ในการตรวจสอบการทำงานของ Euclidian ม <0 จะไม่ได้ผลเพราะ ((นี่% n) + n) n% เป็นบวกเสมอ
bormat

@ รูปแบบใช่มันเป็น แต่ในจาวาสคริปต์%สามารถส่งคืนผลลัพธ์เชิงลบ (นี่คือจุดประสงค์ของฟังก์ชั่นเหล่านี้เพื่อแก้ไข)
zessx

คุณเขียน [code] Number.prototype.mod = function (n) {var m = ((นี้% n) + n)% n; ส่งคืน m <0 หรือไม่ m + Math.abs (n): m; }; [/ code] ให้ค่าหนึ่งค่าของ n โดยที่ m คือnégative พวกมันไม่มีค่าของ n โดยที่ m คือnégativeเพราะคุณเพิ่ม n หลัง% แรก
bormat

โดยไม่ต้องตรวจสอบนี้parseInt(-41).mod(-7)จะกลับมา-6แทน1(และตรงนี้เป็นจุดประสงค์ของฟังก์ชั่นส่วนจำนวนเต็มผมเขียน)
zessx

โอเคคุณพูดถูกทุกคำขอโทษฉันลืมโมดูโลเชิงลบฉันแค่คิดถึงเรื่อง "ลบ" นี้เท่านั้น ฉันขอแนะนำให้ย้าย Math.abs Number.prototype.mod = function (n) {return ((นี้% n) + Math.abs (n))% n; }; (-41) .mod (-7) == 1 // ไม่จำเป็นต้องแยกวิเคราะห์
bormat

0

มีแพ็คเกจ 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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.