+0 และ -0 เหมือนกันหรือไม่


171

อ่านผ่านECMAScript 5.1 สเปค , +0และ-0มีความโดดเด่น

แล้วทำไมไม่+0 === -0ประเมินtrue?


อาจเป็นไปได้ซ้ำกับDifferentiating +0 และ -0
GolezTrol

6
โปรดทราบว่าใน ES2015 คุณสามารถใช้Object.isเพื่อแยกความแตกต่างระหว่าง +0 และ -0
Benjamin Gruenbaum

การอ้างอิง David Flanagan จาก JS คำแนะนำที่ชัดเจน : อันเดอร์โฟล์เกิดขึ้นเมื่อผลลัพธ์ของการดำเนินการเชิงตัวเลขใกล้กับศูนย์มากกว่าจำนวนที่น้อยที่สุดที่สามารถแทนได้ ในกรณีนี้ JavaScript จะคืนค่า 0 หาก underflow เกิดขึ้นจากจำนวนลบ JavaScript จะส่งคืนค่าพิเศษที่เรียกว่า“ ศูนย์ลบ”
RBT

คำตอบ:


193

JavaScript ใช้มาตรฐาน IEEE 754เพื่อแสดงตัวเลข จากWikipedia :

ศูนย์ที่ลงนามคือศูนย์ที่มีเครื่องหมายที่เกี่ยวข้อง ในการคำนวณสามัญ -0 = 0 = 0 แต่ในคอมพิวเตอร์บางการแสดงจำนวนอนุญาตให้มีการดำรงอยู่ของสองศูนย์ที่มักจะแสดงโดย-0 (ลบศูนย์)และ0 (ศูนย์บวก) สิ่งนี้เกิดขึ้นในการแสดงตัวเลขที่ลงนามสำหรับจำนวนเต็มและในการแทนจำนวนจุดลอยตัวส่วนใหญ่ หมายเลข 0 มักจะเข้ารหัสเป็น +0 แต่สามารถแสดงด้วย +0 หรือ −0

มาตรฐาน IEEE 754 สำหรับการคำนวณเลขทศนิยม (ปัจจุบันใช้โดยคอมพิวเตอร์ส่วนใหญ่และภาษาการเขียนโปรแกรมที่รองรับหมายเลขจุดลอย) ต้องใช้ทั้ง +0 และ −0 ศูนย์ถือได้ว่าเป็นตัวแปรของบรรทัดจำนวนจริงที่ขยายเพิ่มเช่น 1 / −0 = −∞ และ 1 / + 0 = + ∞การหารด้วยศูนย์จะไม่ได้กำหนดเฉพาะสำหรับ± 0 / ± 0 และ±∞ / ±∞ .

บทความมีข้อมูลเพิ่มเติมเกี่ยวกับการรับรองที่แตกต่างกัน

นี่คือเหตุผลว่าทำไมในทางเทคนิคศูนย์ทั้งสองจึงต้องแยกแยะ

อย่างไรก็ตาม+0 === -0ประเมินเป็นจริง ทำไม (... )

ลักษณะการทำงานนี้ถูกกำหนดไว้อย่างชัดเจนในส่วน 11.9.6ที่เข้มงวดเท่าเทียมกันเปรียบเทียบอัลกอริทึม (เน้นบางส่วนเหมือง):

การเปรียบเทียบx === yที่xและyเป็นค่าผลิตจริงหรือเท็จ การเปรียบเทียบดังกล่าวดำเนินการดังนี้:

( ... )

  • หาก Type (x) คือ Number แสดงว่า

    1. ถ้า x คือ NaN ให้คืนค่า false
    2. ถ้า y เป็น NaN ให้ส่งคืน false
    3. ถ้า x เป็นค่า Number เดียวกับ y ให้ส่งคืนจริง
    4. ถ้า x เป็น +0 และ y คือ −0 ให้คืนค่าจริง
    5. ถ้า x คือ −0 และ y คือ +0 ให้คืนค่าจริง
    6. กลับเท็จ

( ... )

(ถือเดียวกัน+0 == -0btw.)

ดูเหมือนว่ามีเหตุผลในการรักษา+0และ-0เท่ากัน มิฉะนั้นเราจะต้องคำนึงถึงเรื่องนี้ในรหัสของเราและตัวผมเองไม่ต้องการทำเช่นนั้น;)


บันทึก:

ES2015 Object.isแนะนำวิธีการเปรียบเทียบใหม่ Object.isแยกความแตกต่างอย่างชัดเจนระหว่าง-0และ+0:

Object.is(-0, +0); // false

15
อันที่จริงและ1/0 === Infinity; // true 1/-0 === -Infinity; // true
user113716

48
ดังนั้นเราจึงมี1 === 1และแต่+0 === -0 1/+0 !== 1/-0แปลกจัง!
Randomblue

8
@ สุ่ม: ฉันคิดว่ามันดีกว่า+0 !== -0;) ที่สามารถสร้างปัญหาได้จริงๆ
เฟลิกซ์คลิง

@ FelixKling หรือ0 !== +0/ 0 !== -0ซึ่งจะสร้างปัญหาเช่นกัน!
Yanick Rochon

5
ที่จริงแล้วพฤติกรรมแบบนี้ จำกัด การคำนวณทางคณิตศาสตร์ ตัวอย่างเช่นฟังก์ชัน 1 / x มีค่าอนันต์เป็น 0 อย่างไรก็ตามมันถูกแยกออกจากกันถ้าเราเข้าใกล้ 0 จากด้านบวกของด้านลบ ในอดีตผลลัพธ์คือ + inf ในลำดับหลัง -inf
Agoston Horvath

19

ฉันจะเพิ่มนี่เป็นคำตอบเพราะฉันมองข้ามความคิดเห็นของ @ user113716

คุณสามารถทดสอบ -0 ได้โดยทำสิ่งนี้:

function isMinusZero(value) {
  return 1/value === -Infinity;
}

isMinusZero(0); // false
isMinusZero(-0); // true

6
ควรตรวจสอบ == 0 เช่นกันข้างต้น isMinusZero (-1e-323) จะได้ผลตอบแทนจริง!
คริส

1
@Chris ขีด จำกัด ของเลขชี้กำลังความแม่นยำสองเท่าคือe±308จำนวนของคุณสามารถแสดงได้เฉพาะในรูปแบบ denormalized และการใช้งานที่แตกต่างกันมีความคิดเห็นที่แตกต่างกันเกี่ยวกับสถานที่ที่จะสนับสนุนพวกเขาทั้งหมดหรือไม่ จุดคือในเครื่องบางในบางจุดลอยโหมดหมายเลขของคุณจะแสดงเป็น-0และคนอื่น ๆ 0.000000000000001e-308เป็นจำนวน ลอยตัวได้สนุกมาก
อ่อนแรงตอบสนอง

สิ่งนี้อาจใช้ได้กับภาษาอื่นเช่นกัน (ฉันทดสอบสำหรับ C และงานนี้)
Mukul Kumar

11

ฉันเพิ่งเจอตัวอย่างที่ +0 และ -0 ประพฤติแตกต่างกันมากจริง ๆ :

Math.atan2(0, 0);  //returns 0
Math.atan2(0, -0); //returns Pi

ระวัง: ถึงแม้จะใช้ Math.round กับจำนวนลบเช่น -0.0001 จริง ๆ แล้วก็จะเป็น -0 และสามารถไขการคำนวณที่ตามมาดังที่แสดงไว้ด้านบน

วิธีที่รวดเร็วและสกปรกในการแก้ไขปัญหานี้คือการทำ smth เช่น:

if (x==0) x=0;

หรือเพียงแค่:

x+=0;

นี่จะแปลงตัวเลขเป็น +0 ในกรณีที่เป็น -0


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

ฉันเพิ่งพบสิ่งนี้ใน Math.atan (y / x) เช่นกันซึ่ง (อาจจะน่าประหลาดใจ) สามารถจัดการกับ "y / x" ในเชิงบวกหรือไม่ จำกัด ยกเว้นมันจะให้คำตอบที่ผิดในกรณีที่ x คือ -0 การแทนที่ "x" ด้วย "(x + 0)" แก้ไขได้
Jacob C. พูดว่า Reinstate Monica

5

ในมาตรฐาน IEEE 754 ที่ใช้แทนประเภท Number ใน JavaScript เครื่องหมายจะถูกแทนด้วยบิต (1 หมายถึงจำนวนลบ)

0เป็นผลให้มีอยู่ทั้งลบและบวกค่าสำหรับแต่ละจำนวนที่แทนรวมทั้ง

นี่คือเหตุผลที่ทั้งสอง-0และ+0มีอยู่


3
ส่วนเติมเต็มของทั้งสองใช้บิตสำหรับสัญญาณ แต่มีเพียงหนึ่งศูนย์ (บวก)
เฟลิกซ์คลิง

1
ใช่ แต่ในส่วนเติมเต็มของ Two ค่าลบนั้นก็เป็นส่วนหนึ่งของค่าเช่นกันดังนั้นเมื่อคุณตั้งค่าลบค่าบิตแล้วมันจะไม่เป็นศูนย์อีกต่อไป
Arnaud Le Blanc

3

ตอบชื่อเดิมAre +0 and -0 the same?:

brainslugs83(ในความคิดเห็นของคำตอบโดยSpudley) ชี้ให้เห็นกรณีที่สำคัญที่ +0 และ -0 ใน JS ไม่เหมือนกัน - นำมาใช้เป็นฟังก์ชั่น:

var sign = function(x) {
    return 1 / x === 1 / Math.abs(x);
}

สิ่งนี้จะนอกเหนือจากมาตรฐานMath.signกลับเครื่องหมายที่ถูกต้องของ +0 และ -0


2

มีค่าที่เป็นไปได้สองค่า (การแทนค่าบิต) สำหรับ 0 ค่านี้ไม่ซ้ำกัน โดยเฉพาะอย่างยิ่งในจำนวนจุดลอยตัวนี้สามารถเกิดขึ้นได้ นั่นเป็นเพราะตัวเลขจุดลอยตัวจะถูกจัดเก็บเป็นสูตรจริง

จำนวนเต็มสามารถจัดเก็บในรูปแบบที่แยกกันด้วย คุณสามารถมีค่าตัวเลขด้วยเครื่องหมายบิตเพิ่มเติมดังนั้นในพื้นที่ 16 บิตคุณสามารถเก็บค่าจำนวนเต็ม 15 บิตและเครื่องหมายบิตได้ ในการแทนค่านี้ค่า 1000 (ฐานสิบหก) และ 0000 ทั้งคู่คือ 0 แต่หนึ่งในนั้นคือ +0 และอีกอันคือ -0

สิ่งนี้สามารถหลีกเลี่ยงได้โดยการลบ 1 จากค่าจำนวนเต็มดังนั้นมันอยู่ระหว่าง -1 ถึง -2 ^ 16 แต่สิ่งนี้จะไม่สะดวก

วิธีที่ใช้กันทั่วไปคือการเก็บจำนวนเต็มใน 'การเติมเต็มสองรายการ' แต่ดูเหมือนว่า ECMAscript จะไม่เลือก ในวิธีการนี้ตัวเลขตั้งแต่ 0000 ถึง 7FFF บวก ตัวเลขติดลบเริ่มต้นที่ FFFF (-1) ถึง 8000

แน่นอนว่ากฎเดียวกันนี้ใช้กับจำนวนเต็มที่มากขึ้นด้วย แต่ฉันไม่ต้องการให้ F ของฉันเสื่อมสภาพ ;)


4
แต่คุณไม่พบว่ามัน+0 === -0แปลก ๆ เพราะตอนนี้เรามี1 === 1และ+0 === -0แต่1/+0 !== 1/-0...
Randomblue

2
แน่นอน +0 คือ -0 มันไม่มีอะไรทั้งสองอย่าง แต่มีความแตกต่างอย่างมากระหว่าง + อนันต์และ -infinity มีหรือไม่ ตัวเลขเหล่านั้นอาจเป็นสาเหตุที่ทำให้ ECMA รองรับทั้ง +0 และ -1
GolezTrol

คุณไม่ได้อธิบายว่าทำไมถึง+0 === -0แม้ว่าการรับรองสองบิตจะแตกต่างกัน
Randomblue

1
+0 คือ -0 คือ 0, ไม่มีอะไรเลย, nada, niente มันทำให้รู้สึกว่าพวกเขาเหมือนกัน ทำไมท้องฟ้าถึงเป็นสีฟ้า 4 + 3 ก็เหมือนกับ 1 + 6 แม้ว่าการแสดงจะแตกต่างกัน พวกเขามีการเป็นตัวแทนที่แตกต่างกัน (และทำให้ค่าบิตที่แตกต่างกัน) แต่เมื่อเปรียบเทียบพวกเขาจะถูกจัดการเป็นศูนย์เดียวกันซึ่งพวกเขา
GolezTrol

1
พวกเขาไม่เหมือนกัน ดูstackoverflow.com/questions/7223717/differentiating-0-and-0สำหรับตัวอย่างที่แสดง
Randomblue

2

เราสามารถใช้Object.isในการแยกแยะและ 0 -0 NaN==NaNและสิ่งหนึ่งที่มากขึ้น

Object.is(+0,-0) //false

Object.is(NaN,NaN) //true

1

ฉันโทษมันในวิธีการเปรียบเทียบความเท่าเทียมกันอย่างเข้มงวด ('===') ดูหัวข้อ 4d ป้อนคำอธิบายรูปภาพที่นี่

ดู7.2.13 การเปรียบเทียบความเท่าเทียมอย่างเข้มงวดในข้อกำหนด


0

Wikipedia มีบทความที่ดีที่จะอธิบายปรากฏการณ์นี้: http://en.wikipedia.org/wiki/Signed_zero

กล่าวโดยสรุปคือทั้ง +0 และ -0 ถูกกำหนดไว้ในข้อกำหนดคุณสมบัติ IEEE floating point พวกเขาทั้งสองมีความแตกต่างทางเทคนิคจาก 0 โดยไม่มีสัญญาณซึ่งเป็นจำนวนเต็ม แต่ในทางปฏิบัติพวกเขาทั้งหมดประเมินเป็นศูนย์ดังนั้นความแตกต่างสามารถถูกละเว้นสำหรับวัตถุประสงค์ในทางปฏิบัติทั้งหมด


2
ไม่ถูกต้องทั้งหมด - 1 / -0 == 1/0 ประเมินว่าเป็นเท็จในจาวาสคริปต์เช่น พวกเขาไม่ได้ "ประเมิน" เป็นศูนย์ที่ไม่ได้ลงนามที่มีมนต์ขลังเนื่องจากไม่มีแนวคิดดังกล่าวเช่น "จำนวนเต็มที่ไม่ได้ลงนาม" ใน IEEE 754
BrainSlugs83
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.