Negative Zero (-0) ใน Jasmine
ลองเปรียบเทียบ 0 และ -0 บน Jasmine
คิดว่าโค้ดทดสอบบน jasmine ด้านล่างจะให้ผลลัพธ์เป็นอะไร
const negativeZero: number = -0;
const positiveZero: number = 0;
expect(positiveZero).toEqual(negativeZero);
ลองไปรันจริง ๆ ดูบน stackblitz กัน
ก็จะเห็น error message ที่น่าประหลาดใจว่า “Expected 0 to equal -0”
Social Responses
ลองเอาไปถามใน Facebook ดูเหมือนกัน
ก็ไม่ได้รับความกระจ่างเท่าไหร่ น่าจะเพราะโลกของ Javascript เป็นเหมือนกล่องดำ หรือจักรวาลคู่ขนานที่เข้าใจพฤติกรรมได้ยาก หรือไม่คู่ควรเสียเวลาไปทำความเข้าใจพฤติกรรมมัน
แต่เอาจริง ๆ เรายังต้องใช้มันหากินอยู่ ลองเปิดประตูเข้าไปดูสักหน่อยน่าจะดี
-0 (ลบศูนย์)! คืออะไร
หลังจากที่ไปลอง google ดู ก็เจอว่าใน Javascript มีการ implement data type ที่เป็น Number ตาม standard IEEE Standard for Floating-Point Arithmetic (IEEE 754) ซึ่งบอกไว้ว่าในข้อมูลประเภทที่เป็น floating point (นึกถึงตอนสมัยเรียนนะ ที่มันจะมี bit เรียงกันหน้าตาประมาณตามรูปข้างล่างนี้) มันจะมี bit ซ้ายสุด (sign bit) ที่ทำหน้าที่บอกว่าข้อมูลนี้เป็นค่าติดลบหรือไม่ ดังนั้น ค่าศูนย์ที่เป็นไปได้ มันเลยมีความเป็นไปได้ 2 แบบคือ 0 ที่มี sign bit เป็น 0 (หรือ positive zero) และ 0 ที่มี sign bit เป็น 1 (หรือ negative zero) นั่นเอง
ซึ่งโดยค่าทางคณิตศาสตร์ ค่า 0 และค่า -0 มีค่าเท่ากัน แต่สำหรับภาษาคอมพิวเตอร์แล้ว บางภาษาก็ถูกทำมาให้สามารถมองสองค่านี้ต่างกันได้ ทำให้คณิตศาสตร์ที่ใช้กับจำนวนที่มีค่า -0 ได้ มันมีคุณสมบัติที่เพิ่มมาแบบนี้ได้ด้วย
1/−0 = −∞ และ 1/+0 = +∞ โดยที่ผลหารจะเป็น undefined ในกรณี ±0/±0 และ ±∞/±∞ เท่านั้น
-0 บน Javascript
เรามาลองต่อกันไปอีกนิดด้วย Javascript บน NodeJS เผื่อจะเห็นอะไรมากขึ้น
ตัวอย่างข้างบนทำการทดสอบด้วย operator ==
และ ===
มาเปรียบเทียบความเท่ากัน หลาย ๆ คนน่าจะรู้จักอยู่แล้วว่า operator ==
จะทำการเปรียบเทียบความเท่ากันแบบ Loose equality นั่นคือ ค่าที่เอามาเปรียบเทียบกัน จะถูก convert type ให้เป็น type เดียวกันก่อน ถึงจะทำการ evaluate ความเท่ากัน ส่วน ===
จะทำการเปรียบเทียบแบบ Strict equality ซึ่งจะทำการเปรียบเทียบด้วยวิธีเหมือน ==
เพียงแค่จะไม่มีการ convert type ก่อนเท่านั้น ดังนั้น ถ้าค่าสองค่า ถึงเขียนเหมือนกัน แต่คนละ type ก็จะนับว่าแตกต่างกัน
ลองดูตัวอย่างด้านล่าง น่าจะเห็นภาพชัดขึ้น
จะเห็นได้ว่า ตอนเปรียบเทียบ 0 และ -0 ด้วย ==
และ ===
ต่างก็ให้ผลลัพธ์ true เหมือนกันอย่างที่ควรจะเป็น
แล้ว Jasmine ใช้วิธีเปรียบเทียบแบบไหน?
ถ้าลองเข้าไปดูใน Equality comparisons and sameness จะพบว่า จริง ๆ แล้ว การเปรียบเทียบความเท่ากันใน Javascript ไม่ได้มีแค่ ==
และ ===
แต่ยังมี Object.is()
อีกด้วย แล้ว Jasmine ใช้วิธีนี้หรือ? ลองไปล้วงดู code ของ Jasmine ดูดีกว่าว่าเค้าเปรียบเทียบด้วยอะไร
พอไล่คำสั่ง toEqual ใน Jasmine ไปเรื่อย ๆ จนเจอจุดที่ใช้เปรียบเทียบกัน ก็เจอวิธีที่ Jasmine ใช้ในการเปรียบเทียบ
จะเห็นได้ว่า Jasmine ไม่ได้ใช้ Object.is()
แต่จะใช้วิธีตามที่เขียนไว้ก่อนนี้คือ
1/−0 = −∞ และ 1/+0 = +∞ โดยที่ผลหารจะเป็น undefined ในกรณี ±0/±0 และ ±∞/±∞ เท่านั้น
ดังนั้น ตอนที่ Jasmine เปรียบเทียบค่า 0 กับ -0 เค้าตัดสินใจไปแล้วว่า มันคือคนละตัว ถึงแม้ value เท่ากัน แต่ไม่ identical เพราะเครื่องหมายมันต่างกัน
ในที่สุดก็เข้าใจเสียทีว่าทำไมมันถึงแสดง error แบบนั้นใน Jasmine
ทิ้งท้าย
เอาจริง ๆ ตัว Javascript เองก็มีเรื่องชวนประหลาดใจอยู่หลายอย่าง เกิดจากความหลวม ๆ ของมันเองนั่นแหละที่ทำให้หลาย ๆ เรื่องดูเป็นเรื่องชวนฉงน หลาย ๆ เรื่องกลายเป็นปมด้อยให้คนเขียนภาษาอื่นเอามาล้อกัน สำหรับคนที่ strict มาก ๆ น่าจะไม่ชอบ Javascript กันสักเท่าไหร่ ส่วนคนที่ไม่ strict อาจจะชอบเรื่องที่ท้าทายใน Javascript ก็เป็นไปได้ สำหรับผมแล้ว Javascript มันยังเป็น mainstream อยู่ ดังนั้นมันน่าจะไม่เสียหาย ถ้าเราจะไปทำความรู้จักพฤติกรรมมันให้มากขึ้นอีกหน่อย จะได้ใช้ชีวิตไปด้วยกันได้ยาว ๆ