/ 365วันแห่งโปรแกรม

[365 วันแห่งโปรแกรม #day34] Signed & Unsigned number

วันที่สามสิบสี่ของ ‪#‎365วันแห่งโปรแกรม วันนี้เราจะคุยกันเรื่อง Signed & Unsigned number


Signed & Unsigned number คืออะไร?

หลายคนอาจจะเคยเขียนโปรแกรมในภาษาที่มี number ทั้งแบบ Signed และ Unsigned มาบ้างแล้ว ซึ่ง number สองชนิดนี้ต่างกันตรงที่ แบบ Signed จะสามารถเก็บค่าที่เป็นลบได้ ส่วน Unsigned เก็บค่าลบไม่ได้ แต่จะเก็บค่าบวกได้เป็น 2 เท่าของแบบ Signed แล้วทำไมถึงเป็นแบบนั้นล่ะ? ก็เพราะ Signed number และ Unsigned number มีวิธีจัดเก็บบน memory ต่างกันนั่นเอง

Signed number มีหน้าตายังไงบน Memory

อย่างที่เรารู้กันดีว่าจริงๆ แล้ว Memory นั้นเก็บข้อมูลบเป็น bit ซึ่งมีสองสถานะเขียนแทนด้วย 0 และ 1 นั่นเอง การจัดเก็บตัวเลขแบบ Signed นั้นมีอยู่ด้วยกันหลายแบบได้แก่

Signed magnitude

Signed magnitude เป็นวิธีที่เข้าใจง่ายที่สุดในการจัดเก็บ ในวิธีนี้ใช้หลักการง่ายๆ ว่าให้บิตทางซ้ายสุดเก็บค่าว่าเป็นลบหรือบวก แล้วบิตที่เหลือเก็บค่าของตัวเลข เริ่มจากเลข 0 ทั้งหมด ทั้งฝั่งลบและบวก ดังนั้นตัวเลขขนาด 8 bit จะสามารถเก็บค่าได้ดังนี้

+127 = 01111111
...
+16  = 00010000
...
+1   = 00000001
+0   = 00000000
-0   = 10000000
-1   = 10000001
...
-16  = 10010000
...
-127 = 11111111

จะเห็นว่าสามารถเขียนและเข้าใจได้ง่ายมาก แต่ว่าทำไมมี 0 สองตัวล่ะ ทั้งที่ 0 ควรมีตัวเดียว ไม่ใช่มีทั้งบวกและลบ ปัญหาของวิธีนี้คือใช้ตัวเลขกำหนดค่าบวกลบเพียงอย่างเดียวนอกนั้นใช้ค่าเดิมนั่นเอง

Ones' complement

Ones' complement เป็นวิธีเก็บค่าอีกแบบ ที่ใช้ bitwise NOT apply ลงไปในค่าที่เป็นบวกแล้วเราจะได้ค่าที่เป็นลบออกมาแทน เช่น เรามีตัวเลข 00000001 ซึ่งเท่ากับ 1 ฐานสิบ ถ้าเราต้องการเลข -1 ก็แค่ใช้ bitwise NOT กับทุกบิตของจำนวน 00000001 ก็จะได้ว่า 11111110 เท่ากับ -1 นั่นเอง

+0   = 00000000
+1   = 00000001
...
+16  = 00010000
...
+127 = 01111111
-127 = 10000000
...
-16  = 11101111
...
-1   = 11111110
-0   = 11111111

วิธีนี้ก็ยังมีปัญหาเดิมอยู่ครับ คือมี +0 กับ -0 อยู่ดี

Two's complement

เมื่อยังมีปัญหาเรื่องเลข 0 ก็เลยมีคนคิดวิธีใหม่ขึ้นมา เรียกว่า Two's complement วิธีนี้จะใช้บิตซ้ายสุดบอกว่าเป็นบวกหรือลบเหมือนวิธีแรก และกำหนดให้เลข 0 มีค่าเป็น 00000000 และจำนวนบวกจะเริ่มจาก 00000001 ซึ่งมีค่าเป็นหนึ่ง ขึ้นไปเรื่อยๆ ถึง 01111111 ซึ่งมีค่า 127 ส่วนจำนวนลบจะเกิดจาก การกลับบิตด้วย bitwise NOT แล้วเพิ่มค่าให้มันหนึ่ง เช่นจาก 00000001 ก็กลับเป็น 11111110 แล้วเพิ่มค่า 1 จะได้ 11111111 ซึ่งมีค่าเป็น -1

+0   = 00000000
+1   = 00000001
...
+16  = 00010000
...
+127 = 01111111
-128 = 10000000
-127 = 10000001
...
-16  = 11110000
...
-2   = 11111110
-1   = 11111111

จะเห็นว่าวิธีนี้จะเหลือ 0 เพียงตัวเดียว เมื่อเราพยายามกลับบิตของ 0 แล้วจะเกิด overflow ขึ้น และบิตที่เหลือจะยังเป็น 0 ทั้งหมดเหมือนเดิม ทำให้เรามี 0 เพียงตัวเดียว แต่ในขณะเดียวกันก็ยังมีอีกค่าหนึ่งที่ทำ Two's complement แล้วเกิด overflow ก็คือค่าสุดท้ายที่มี 1 เป็นบิตซ้ายสุดและบิตที่เหลือเป็น 0 ซึ่งเราถือว่าค่านี้คือจำนวนฝั่งลบที่ติดลบมากที่สุด (-128 ในระบบ 8 บิต) เพราะบิตซ้ายสุดเป็น 1 นั่นเอง

Excess-K

Excess-K นั้นจะคล้ายๆ กับวิธี Two's complement แต่ว่าถ้าบิตซ้ายสุดเป็น 1 จะหมายถึงเป็นค่าบวกแทน และจำนวนลบจะเกิดจาก นำจำนวนบวกไปลบหนึ่งแล้วทำ complement เช่น จากเลข 1 คือ 10000001 จะได้ค่า -1 คือ 01111111

-128 = 00000000
-127 = 00000001
...
-16  = 01110000
...
-1   = 01111111
0    = 00000000
+1   = 10000001
...
+16  = 10010000
...
+127 = 11111111

จะเห็นว่าวิธีนี้ก็ไม่ต่างจากวิธี Two's complement เท่าไหร่ครับ เรื่องเลข 0 ไม่มีปัญหาเหมือนกัน เหลือจำนวนลบอีกตัวเหมือนกัน

จริงๆ ก็ยังมีวิธีจัดเก็บตัวเลข Signed number แบบอื่นอีก แต่ว่าเท่านี้เราก็น่าจะเห็นภาพแล้วว่าการจัดเก็บตัวเลขแบบคิดเครื่องหมายนั้น ปัญหาก็คือตัวเครื่องหมายนั่นเอง คราวนี้เราไปลองดู Unsigned number กันบ้างดีกว่าครับ

Unsigned number มีหน้าตายังไงบน Memory

สำหรับ Unsigned นั้นง่ายเลยครับ เพราะไม่มีเครื่องหมายมาให้ปวดหัวอีกแล้ว ดังนั้นการเขียนเป็นเลขฐานสองจึงตรงไปตรงมาเลย เขียนไปตรงๆ ตั้งแต่ 00000000 แล้วเพิ่มขึ้นเรื่อยๆ ดังนี้

0   = 00000000
1   = 00000001
2   = 00000010
...
16  = 00010000
...
128 = 10000000
...
255 = 11111111

แล้วในโปรแกรมเราประกาศยังไงว่าอันไหน Signed อันไหน Unsigned

โดยทั่วไปแล้วภาษาโปแกรมจะมี keyword ว่า unsigned มาให้ สำหรับใช้บอกว่าตัวแปรนั้นเป็น unsigned นะ ถ้าไม่ใส่ก็ถือว่าเป็น signed ครับ

unsigned int a = 0;
int b = 0;

แต่ก็มีบางภาษาโปรแกรมที่ไม่ยอมให้มี unsigned เลยก็มีครับ เช่น Java

แล้วใช้ Unsigned ดีไหม?

ถ้าเรามั่นใจว่าไม่ได้ใช้ค่าลบแน่ๆ ก็ใช้ไปเถอะครับเพราะมันจะทำให้เก็บค่าบวกได้เพิ่มขึ้น แต่ก็เนื่องจากว่าบางภาษาโปรแกรมก็ไม่มี ดังนั้นถ้าอยากจะเขียนให้สอดคล้องกันทุกแพลตฟอร์มก็เลี่ยงไปใช้ signed แทนก็ได้ครับ

‪#‎day34 #365วันแห่งโปรแกรม ‪#‎โครงการ365วันแห่ง‬...