| โดย เม้ง
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1. เกริ่นนำ
บทความนี้เราจะมาพูดกันถึงชนิดของตัวแปร (variable type) ที่จาวามีให้ใช้ได้ทันที ส่วนนี้จะเป็นส่วนที่สำคัญมากเพราะจะเป็นพื้นฐานให้กับการเขียนโปรแกรมในทุกภาษา โดยเฉพาะผู้ที่เขียนโปรแกรมที่ต้องมีการเกี่ยวข้องกับคำนวญทางคณิตศาสตร์ ถ้าคุณผู้อ่านไม่ทำความเข้าใจกับการใช้ตัวแปร (variable) เหล่านี้ล่ะก็จะทำให้เกิดความผิดพลาดในตัวโปรแกรมและจะเป็นบั๊ก (bug) ที่หาค่อนข้างยาก ในบทความนี้ผู้เขียนพยายามเน้นในเรื่องของ complement และตัวแปรที่เป็นตัวเลขเสียส่วนมากเพราะคิดว่าคุณผู้อ่านบางท่านคงจะสนใจหรือไม่เคยรู้ในส่วนนี้มาก่อน ผู้เขียนหวังว่าคงจะเกิดประโยชน์แก่คุณผู้อ่านที่จะเอาความรู้นี้ไปใช้ในการเขียนโปรแกรมที่ต้องการใช้การคำนวณเพื่อให้เกิดความผิดพลาดน้อยที่สุด 2. วัตถุประสงค์
3. การประกาศ การตั้งค่า และการตั้งค่าเริ่มต้นของตัวแปร
type varname1, varname2, ... ; ในการประกาศตัวแปรนั้นประกอบไปด้วยสองส่วนใหญ่ๆคือ ชนิดของตัวแปร (type) และชื่อของตัวแปร (varname) โปรดสังเกตว่าที่ท้ายของบรรทัดนั้นจบด้วยเครื่องหมาย ";" ในจาวานั้นทุกบรรทัดที่เป็นการประกาศตัวแปรหรือฟังก์ชันนั้นจะต้องจบด้วยเครื่องหมายนี้ เราสามารถประกาศตัวแปรหลาย ๆ ตัวพร้อม ๆ กันได้ภายในบรรทัดเดียวดังตัวอย่างข้างบน ตัวแปรที่อยู่ในบรรทัดนั้นจะเป็นชนิดเดียวกันทั้งหมด ในจาวานั้นหลังจากที่คุณผู้อ่านประกาศตัวแปรแล้วจาวาจะใส่ค่าเริ่มต้น (initialize) ให้ตัวแปรเหล่านั้นทันที เพราะฉะนั้นคุณผู้อ่านก็สามารถรู้ได้ทันทีว่าตัวแปรที่คุณผู้อ่านประกาศไว้นั้นมีค่าเท่าใหร่ แต่คุณผู้อ่านไม่ควรจะเชื่อค่าเหล่านั้นเพราะค่าเหล่านี้อาจจะถูกเปลี่ยนในอนาคตก็ได้ผู้เขียนจึงขอเสนอว่าให้คุณผู้อ่านตั้งค่าเริ่มต้นด้วยตนเองเสมอ ถ้าคุณผู้อ่านต้องการทั้งประกาศและตั้งค่าเริ่มต้นด้วยในบรรทัดเดียวกันก็สามารถทำได้ดังนี้ type varname1=varvalue1, varname2=varvalue2, ... ; การตั้งค่าของตัวแปร (assignment) นั้นก็คือการก๊อปปี้ค่าใส่เข้าไปในตัวแปรนั้นนั่นเอง ตัวแปรที่จะถูกตั้งค่านั้นจะต้องเป็นตัวแปรที่ถูกประกาศมาแล้วและจะต้องอยู่ทางซ้ายของเครื่องหมายเท่ากับ (=) และค่าที่จะใส่เข้าไปในตัวแปรนั้นต้องอยู่ทางขวาของเครื่องหมายเท่ากับ varname=varvalue; นอกจากจะตั้งค่าของตัวแปรด้วย value แล้วคุณยังสามารถตั้งค่าด้วยตัวแปรอื่นด้วย เช่น varname1=varname2=varname3=varvalue1; ในบรรทัดข้างบนนี้จาวาจะก๊อปปี้ค่าจากทางขวาสุดไปทางซ้ายสุด เพราะฉะนั้นค่าของ varname1varname2 และ varname3 จะมีค่าเท่ากันซึ่งเท่ากับ varvalue1 ข้อกำหนดของการตั้งค่าตัวแปรนั้นตัวแปรและค่าที่จะตั้งนั้นต้องเป็นชนิดเดียวกัน ยกตัวอย่างเช่น a=20, ตัวแปร a นั้นต้องเป็นตัวแปรชนิด integer มีข้อยกเว้นบางประการที่ตัวแปรและค่าที่จะตั้งอาจไม่ต้องเป็นชนิดเดียวกันแต่จะยกไปพูดในเรื่องของการ casting ส่วนทางด้านซ้ายของเครื่องหมายเท่ากับจะต้องเป็นตัวแปรที่สามารถใส่ค่าได้เท่านั้น ถ้าคุณลองคอมไพล์ไฟล์ที่มีบรรทัด 50=50; จาวาคอมไพเลอร์จะฟ้องว่ามี error 4. การตั้งชื่อของตัวแปร
5. ตัวแปรที่คุณผู้อ่านควรจะรู้จัก
ตารางที่ 1 ชนิดและขนาดของตัวแปรต่าง ๆ
จริงๆแล้วเรื่อง complement นี้ผู้ที่เขียนโปรแกรมไม่จำเป็นต้องรู้ในรายละเอียดก็ได้ แต่ผู้เขียนเองอยากจะเขียนไว้ให้เป็นเกร็ดความรู้แด่คุณผู้อ่าน ถ้าคุณผู้อ่านท่านใดที่ทราบเกี่ยวกับ complement แล้วหรือไม่สนใจในรายละเอียดก็สามารถข้ามหัวข้อที่ 6 และ 7 ไปได้โดยไม่ขาดความต่อเนื่องนะครับ 6. Complements
ส่วนเติมเต็มของน้ำในแก้ว = ปริมาตรของแก้ว - ปริมาณน้ำที่มีอยู่ในแก้วก่อนแล้ว ทีนี้เราก็จะมาเปรียบเทียบตัวอย่างในเรื่องของน้ำข้างต้นให้เข้ากับเรื่องของตัวเลขฐานสองของเรานะครับ
complement = (2^N)-1 - X ข้างบนนี้คือคำจำกัดความอย่างง่ายๆของ one's complement เราลองมายกตัวอย่างเล็ก ๆ กันดีกว่านะครับ ยกตัวอย่างตัวเลข 3-bit 0112 หรือ 3 ค่าที่มากที่สุดที่จะใส่เข้าไปในเลข 3-bit ได้นั้นก็คือ (2^3)-1=8-1=7=1112 (เลขสองที่ห้อยอยู่ท้ายตัวเลขนั้นแสดงว่าตัวเลขที่อยู่ข้างหน้ามันเป็นเลขฐานสอง ถ้าไม่มีเครื่องหมายนี้หมายความว่าเป็นเลขฐานสิบ) คุณจะสังเกตเห็นว่าค่ามากสุดที่จะใส่ เข้าไปในตัวเลข N-bit นั้นก็คือ 111....112 เสมอ แต่จำนวนเลขหนึ่งที่มีอยู่ในนั้นก็คือ N ตัว คุณควรจะพิสูจน์ดูด้วยตนเอง one's complement of a 3-bit number 112 = 1112-0112 = 1002 คุณจะสังเกตเห็นว่า one's complement นั้นก็คือการกลับตัวเลขจากศูนย์เป็นหนึ่งและจากหนึ่งเป็นศูนย์นั่นเองแต่ว่าคุณต้องเติมเลขศูนย์ข้าง หน้าของตัวเลขที่จะทำ one's complement ให้เต็มจำนวน bit ที่ต้องการเสียก่อน จากตัวอย่างข้างต้นเราต้องเติมศูนย์เข้าไปหนึ่งตัวเพื่อให้ 112 กลายเป็นเลข 3-bit ทีนี้เราก็จะมาคุยกันถึง two's complement กันบ้างล่ะ two's complement ใกล้เคียงกับ one'c complement มากเพียงแต่เพิ่มค่ามากสุดขึ้นมาอีก 1 เพราะฉะนั้นแทนที่ค่ามากสุดจะเป็น (2^N)-1 หรือ 111....11 ที่มีเลขหนึ่งเท่ากับ N ตัว ก็จะเปลี่ยนเป็น 2^N หรือ 100....00 ที่ มีศูนย์เท่ากับ N ตัว คุณผู้อ่านคงจะสังเกตเห็นว่าจำนวน bit ของค่ามากสุดในกรณีนี้มากกว่า N ไป 1-bit ถ้าเราเอามาเขียนเป็นสมการก็จะได้ว่า two's complement = one's complement + 1
ถ้า X คือค่าที่จะทำ complement และ N คือจำนวน bit ที่สนใจ คุณคงจะสังเกตเห็นได้ว่าการทำ two's complement ที่ง่ายที่สุดก็คือการทำ one's complement (กลับศูนย์เป็นหนึ่งและหนึ่งเป็นศูนย์) แล้วจึงบวกหนึ่งเข้าไป ในบางกรณีหลังจากคุณทำ two's complement แล้วอาจจะได้่ตัวเลขที่มีจำนวน bit เท่ากับ N+1 แต่เราจะยังคงสนใจแค่ N-bit เพราะฉะนั้นเราจะตัด bit ที่เพิ่มขึ้นมาออก อีกอย่างหนึ่งที่คุณผู้อ่านควรจะรู้เกี่ยวกับ complement ก็คือถ้าเรามีเลขฐานสองอยู่ตัวหนึ่งแล้วเราทำ complement สองครั้ง เราจะได้ตัวเลขตัวเดิมกลับมา ลองพิสูจน์ด้วยตนเองดูนะครับ 7. Complement มีไว้เพื่อใช้ทำอะไรใครรู้บ้าง
ทีนี้เรามี complement สองชนิดให้เลือก เรามาลองด้วย one's complement
กันก่อนนะครับ
ค่าลบของเลขฐานสอง = one's complement ของเลขฐานสองที่เป็นค่าบวก แล้วเราก็มาดูตัวอย่างกัน ยกตัวอย่างเดียวกับข้างบน ตารางที่ 2 one's complement ของตัวเลขฐานสอง 4-bit
ทีนี้ก็ลอง two's complement ครับ ค่าลบของเลขฐานสอง = two's complement ของเลขฐานสองที่เป็นค่าบวก ตารางที่ 3 two's complement ของตัวเลขฐานสอง 4-bit
คุณผู้อ่านสังเกตเห็นความแตกต่างอะไรบางอย่างในสองกรณีข้างบนใหมครับ ในกรณีที่ตัวเลขเท่ากับ 00002 two's complement ของมันคือตัวเดียวกัน (เลขฐานสองของศูนย์และลบศูนย์คือตัวเดียวกัน) เท่ากับ 00002 แต่สำหรับ one's complement นั้นไม่ใช่ เพราะฉะนั้นในความเห็นของผู้เขียนนั้น two's complement ดูจะเหมาะสมมากกว่าในการแทนค่าลบเรามาลองจัดตารางข้างบนนี้ใหม่ให้ดูง่ายขึ้นกันนะครับ ตารางที่ 4 เลขฐานสองที่ใช้แทนค่าลบโดยใช้ two's complement
จาก 10002 ในตารางข้างบนนี้เป็นกรณีพิเศษ - ค่า two's complement ของ 10002 ก็คือ 10002 ซึ่งก็คือตัวมันเองทีนี้เราจะให้มันเป็นค่า +8 หรือ -8 ดีล่ะครับ เนื่องจากว่าค่าลบที่เหลือในตารางนั้นขึ้นต้นด้วยเลข 1 เพราะฉะนั้นจะเป็นการดีที่ให้ 10002 แทนค่า -8 เพราะฉะนั้นเราจึงได้บทสรุปแล้วว่าถ้า bit แรกหรือ most significant bit (msb) เป็นค่า 1 เราก็รู้ได้เลยว่าเลขฐานสองนั้นเป็นค่าลบ เพราะฉะนั้น bit นี้จึงถูกเรียกว่า sign bit และถ้าเราพูดถึงตัวเลข N-bit แบบมีเครื่องหมาย (signed) ค่ามากสุดที่เราจะใส่เข้าไปได้ในเลขตัวนั้นก็คือ (2^(N-1))-1 และค่าน้อยสุดก็คือ -1*((2^(N-1))) 8. byte, short, int and long
โปรแกรมที่ 1
System.out.println("Start byte test");
ให้ลองสังเกตผลลัพธ์ของโปรแกรมนี้ดูนะครับ คุณจะเห็นว่าหลังจากที่เราเพิ่มค่าของ bytevar ขึ้นไปทีละหนึ่งนั้น ค่าของมันเริ่มวิ่งจาก 1 (000000012) เพิ่มขึ้นจนถึง 127 (011111112) หลังจากนั้นก็กลายเป็นค่าลบ -128 (100000002) และตามด้วย -127 (100000012) และเพิ่มขึ้นไปจนถึง -1 (111111112) และกลับมาที่ 0 (000000002) ตามลำดับ สิ่งที่คุณผู้อ่านควรจะระวังอย่างมากคือการใช้ตัวแปรแบบ byte เก็บค่าที่เป็นตัวนับ (counter) คุณผู้อ่านควรจะมั่นใจว่าการเพิ่มของตัวนับนั้นไม่เกินค่ามากที่สุดของตัวแปรแบบนั้น มิฉะนั้นมันจะกลายเป็นค่าลบและทำให้การคำนวณผิดพลาดไป คุณผู้อ่านสามารถเอาตัวอย่างข้างต้นไปลองกับตัวแปร ประเภทจำนวนเต็มชนิดอื่นๆอย่างเช่น short, int และ long โดยการเปลี่ยนตัวแปร bytevar ให้เป็นตัวแปรชนิดนั้นๆ ส่วนชนิดของตัวแปรที่ใช้เป็นตัวนับนั้นควรจะเป็นตัวแปรที่ใหญ่กว่าเนี่องจากว่าเราจะต้องครอบคลุมค่าลบทั้งหมดด้วย 9. float and double
การเก็บค่าของ float และ double นั้นไม่เหมือนกับการเก็บค่าจำนวนเต็มที่เราเคยเห็นใน byte, short, int และ long ใน 32-bit ของ float นั้นจะถูกแบ่งออกเป็นสามส่วน ส่วนแรกเรียกว่า sign bit มีขนาด 1-bit ส่วนที่สองเรียกว่า exponent ขนาด 8-bit และส่วนที่สามเรียกว่า mantissa ขนาด 23-bit ในมาตรฐานของ IEEE-754 นั้นทั้งสามส่วนจะถูกเรียงลงในเลขขนาด 32-bit ดังนี้ s eeeeeeee fffffff ffffffffffffffff โดยที่ s แทน sign bit, e แทน exponent และ f แทน mantissa ส่วนตัวแปรแบบ double นั้นในมาตรฐานของ IEEE-754 นั้นวางรูปแบบเหมือนกับ float แต่ต่างกันที่ exponent มี 11-bit และ mantissa มี 52-bit โดยทั่วไปแล้วค่า float และ double นั้นจะถูกคำนวณดังนี้ -1^(s)*(2^eee....e)*(1.fff....f) โดยที่ eee....e เป็นเลขฐานสอง 2^eee....e คือ สองยกกำลัง eee....e และ 1.fff....f คือการเอาเลขหนึ่งเติมเข้าไปข้างหน้า mantissa แล้วเติมจุดทศนิยมคั่นตรงกลาง การคำนวณเลขฐานสองแบบมีจุดทศนิยมก็เหมือนกับแบบไม่มีทศนิยมเพียงแต่ว่าหลังจากจุดทศนิยมให้คูณด้วยสองยกกำลังติดลบเท่านั้นเอง ยกตัวอย่างเช่น 101.110012 = 1*2^2+0*2^1+1*2^0+1*2^-1+1*2^-2+0*2^-3+0*2^-4+1*2^-5 = 5.78125จากการสูตรการคำนวณตัวแปรชนิด float และ double ข้างบนั้นเราจะเห็นว่ามันอยู่ในรูปของ เครื่องหมายบวกหรือลบคูณด้วยค่าหนึ่งกว่าๆและคูณด้วยสองยกกำลังบางอย่างซึ่งไม่เกิน 2^127 มาถึงจุดนี้แล้วคุณผู้อ่านควรจะลองคำนวณค่ามากสุดและค่าน้อยสุดดูว่าตรงกับในตารางหรือเปล่า 10. boolean
โปรแกรมที่ 2
11. String
String เป็นออบเจ็กที่ถูกออกแบบมาให้เก็บตัวอักษรหลายๆตัวเข้ามาอยู่ในตัวออบเจ็กเดียวกัน หลังจากที่เราใส่ข้อความตัวอักษรเข้าไปในตัวแปรแบบ String แล้วเราจะไม่สามารถแก้ค่านั้นได้ String stringA = "A simple test string"; // Create new String เราสามารถทำให้สองตัวแปรแบบ String มีค่าเท่ากันได้ดังในตัวอย่าง String stringB = stringA; // Assign one string to another เนื่องจาก String นั้นเป็นออบเจ็ก ตัวแปร stringB นั้นจะชี้ไปหาค่าเดียวกันกับ stringA String ที่ได้ถูก assigned ไปแล้วไม่สามารถแก้ได้แต่เราสามารถ assign ค่าอื่นให้กับ String ตัวนั้นได้ และไม่ต้องกังวลว่าจะเกิด memory leak อีกด้วย stringA = "Another test string"; เราสามารถต่อ String สองตัวเข้าด้วยกันเป็น String ตัวที่สามได้อย่างเช่น String stringC = stringA + stringB; ถ้าคุณผู้อ่านอยากจะ print ค่าของตัวแปรแบบ String ก็สามารถทำได้ดังนี้ System.out.println(stringA); หรือถ้าคุณผู้อ่านอยากจะ print สอง String ติดกันก็ให้เรียกคำสั่ง System.out.println(stringA + stringB); 12. Literal Constant
L,l -> long
ยกตัวอย่างเช่น long along = 345L;
เรายังสามารถบอกคอมไพเลอร์ให้แปรค่า literal constant เป็นอย่างอื่นได้อีก ยกตัวอย่างเช่น 0X(zero and X) ใช้เป็น prefix ของค่า hexadecimal (เลขฐานสิบหก) หรือ 0(zero) เป็น prefix ของค่า octal (เลขฐานแปด) ดังตัวอย่างข้างล่างนี้ int anint = 0X1A2F; // assign an int with a hexadecimal 1A2F
13. Casting
float a=20.5;
ในบรรทัดที่สองนั้นมีความหมายว่าเอาค่า int ที่อยู่ใน a มาใส่ในตัวแปรชั่วคราวแบบ int ที่ไม่มีชื่อแล้วใส่เข้าไปในค่า b ซึ่งเป็นตัวแปรแบบ int คุณผู้อ่านจะสังเกต เห็นว่า a นั้นเป็นตัวแปรแบบ float เพราะฉะนั้นค่าหลังจากจุดทศนิยมนั้นก็จะหายไปหลังจากที่อยู่ใน b แล้ว (โปรดสังเกตว่า a นั้นก็ยังคงเป็น float อยู่เหมือนเดิม) โดยปรกติแล้วในการทำ assignment นั้นจาวาจะทำ casting ให้โดยอัตโนมัติ แล้วทำไมเราถึงต้องมี explicit casting ล่ะ ผู้เขียนจะขอยกตัวอย่างเช่นถ้าเราลองหาร ตัวเลขแบบ int สองตัวแล้วใส่เข้าไปในค่า float int x=10, y=3;
ค่าหลังจุดทศนิยมที่ได้จากการหารนั้นจะหายไปเนื่องจากว่าทั้ง x และ y เป็น int เพราะฉะนั้นผลของการหารก็เลยกลายเป็น int ไปด้วย ถ้าเราจะให้ผลของการหาร เป็น float ล่ะก็อย่างน้อยตัวแปรตัวใดตัวหนึ่งของการหารจะต้องเป็น float เราจึงต้องทำแบบนี้ float z = (float)x/y หรือ float z = x/(float)y หรือ float z = (float)x/(float)y 14. final variable
final int a=20;
15. สิ่งที่คุณผู้อ่านควรจะได้รับหลังจากอ่านบทความนี้
|