ข้อมูลจัดเป็นองค์ประกอบที่สำคัญในการประมวลผลโปรแกรม คำสั่งในภาษาคอมพิวเตอร์ส่วนใหญ่เป็นคำสั่งที่ดำเนินการจัดการกับข้อมูล ในการเรียนรู้การเขียนโปรแกรม (ไม่ว่าจะใช้ภาษาคอมพิวเตอร์ใดก็ตาม) ผู้เขียนโปรแกรมจำเป็นต้องทำความเข้าใจกับข้อมูลชนิดต่างๆ ที่มีในภาษาคอมพิวเตอร์นั้น รวมถึงศึกษาวิธีการจัดการกับชนิดข้อมูลแต่ละประเภท เพื่อที่จะเขียนคำสั่งการทำงานกับข้อมูลดังกล่าวได้อย่างถูกต้อง ในบทนี้ จะกล่าวถึงชนิดข้อมูลมูลฐานชนิดต่างๆ ที่มีในภาษา C++ การประกาศข้อมูล ลักษณะการเขียนข้อมูล การเรียกใช้งานข้อมูล รวมถึง ตัวดำเนินการที่กระทำกับข้อมูลในแต่ละประเภท ในท้ายบท จะกล่าวถึง ปัญหาที่มักพบในการจัดการข้อมูลทางคอมพิวเตอร์ โดยเฉพาะข้อมูลชนิดเลขจำนวน เช่น ปัญหาการล้นของข้อมูลชนิดเลขจำนวน และ ข้อผิดพลาดการปัดเศษของข้อมูลชนิดจุดลอยตัว
ชนิดข้อมูลมูลฐานในภาษา C++ แบ่งได้เป็น 2 ประเภท คือ ชนิดเลขจำนวนเต็ม (integral types) และ ชนิดจุดลอยตัว (floating-point types) โดยที่ ข้อมูลชนิดเลขจำนวนเต็มเป็นค่าที่แน่นอน ใช้สำหรับการนับ ในขณะที่ข้อมูลจุดลอยตัวเป็นค่าโดยประมาณทางคอมพิวเตอร์ ใช้สำหรับการวัดค่า
ชนิดข้อมูลมูลฐานในภาษา C++ มีทั้งสิ้น 14 ชนิด แบ่งออกเป็น ชนิดเลขจำนวนเต็ม 11 ชนิด และชนิดจุดลอยตัวอีก 3 ชนิด ดังรูปที่ 2-1 ชนิดจำนวนเต็ม ประกอบด้วย ชนิดตรรกะ (boolean type: bool) ชนิดแจงนับ (enumeration types: enum) ชนิดอักขระ (chracter types: char, unsigned char และ wchar_t) และ ชนิดจำนวนเต็ม (integer types: short, int, long, unsigned short, unsigned int และ unsigned long) สำหรับชนิดจุดลอยตัว ประกอบด้วย float, double และ long double
ชนิดตรรกะ (boolean type) เป็นข้อมูลเลขจำนวนเต็มที่ประกอบด้วยค่าที่เป็นไปได้ 2 ค่า คือ false และ true ซึ่งเก็บค่าจำนวนเต็ม 0 และ 1 ตามลำดับ ข้อมูลชนิดตรรกะในภาษา C++ มาตรฐาน ได้แก่ ข้อมูลชนิด bool
ตัวอย่าง 2.1 ตัวแปรตรรกะ
int main ( ) { // prints the value of boolean variable bool flag = false; cout << "flag = " << flag << endl; flag = true; cout << "flag = " << flag << endl; }
ทดสอบโปรแกรม
flag = 0 flag = 1
ให้สังเกตุว่า ค่า false จะแสดงผลเป็นจำนวนเต็ม 0 ทางจอภาพ ในขณะที่ ค่า true จะแสดงผลเป็นจำนวนเต็ม 1 ทางจอภาพ
ชนิดแจงนับ(enumeration types) เป็นชนิดข้อมูลในภาษา C++ ที่อนุญาตให้ผู้เขียนโปรแกรมสามารถกำหนดรูปแบบของชนิดข้อมูลได้เอง โดยข้อมูลดังกล่าวจะต้องประกอบด้วยค่าที่เป็นจำนวนเต็ม
ชนิดอักขระ (character types) เป็นข้อมูลเลขจำนวนเต็มชนิดหนึ่ง ที่แทนค่าตัวอักขระ อันได้แก่ ตัวอักษรตัวพิมพ์ใหญ่ ตัวอักษรตัวพิมพ์เล็ก ตัวเลข สัญลักษณ์และเครื่องหมายต่างๆ รวมถึง อักขระควบคุมการแสดงผล เช่น 'A', 'a', '1', '$' และ '\n' เป็นต้น ภาษา C++ ใช้รหัส ASCII ในการแทนค่าตัวอักขระแต่ละตัว โดยตัวคงที่อักขระจะเขียนภายในเครื่องหมาย ' '
ตัวอย่าง 2.2 ตัวแปรอักขระ
int main ( ) { // prints character and its integer value char c = 'A'; count << "c = " << c << " , int (c) = " << int(c) << endl; c = 't'; count << "c = " << c << " , int (c) = " << int(c) << endl; c = '\t'; // tab character count << "c = " << c << " , int (c) = " << int(c) << endl; c = '!' ; count << "c = " << c << " , int (c) = " << int(c) << endl; }
ทดสอบโปรแกรม
c = A , int (c) = 65 c = t , int (c) = 116 c = , int (c) = 9 c = ! , int (c) = 33
แม้ว่าข้อมูลชนิดอักขระในภาษา C++ จะเป็นข้อมูลจำนวนเต็ม แต่การรับเข้าและส่งออกข้อมูลอักขระ จะกระทำในรูปของตัวอักขระ ไม่ใช่ค่าจำนวนเต็ม จากตัวอย่าง เมื่อตัวแปรเก็บค่าอักขระ 'A' การแสดงผลตัวแปร c จะปรากฏอักขระ A ทางจอภาพ ไม่ใช่ค่าจำนวนเต็ม 65 ในกรณีที่ผู้เขียนโปรแกรมต้องการนำค่าจำนวนเต็มของตัวแปรชนิดอักขระมาแสดงผล จะต้องใช้ตัวดำเนินการแคสชนิดข้อมูล (type casting operator) int ( ) เช่น int (c) และ เมื่อตัวแปร c เก็บค่าอักขระ 'A' ก็จะแสดงค่าจำนวนเต็ม 65 ทางจอภาพ ซึ่งค่าจำนวนเต็ม 65 คือ ค่ารหัสของอักขระ 'A' ตามการแทนค่าของรหัสแอสกี
ในภาษา C++ ข้อมูลชนิดจำนวนเต็มมี 6 ประเภท คือ short, int, long, unsigned short, unsigned int และ unsigned long ข้อมูลแต่ละชนิดจะมีขนาดที่แตกต่างกัน ทำให้เก็บค่าจำนวนเต็มได้ในช่วงที่ต่างกัน โปรแกรมตัวอย่าง 2.3 แสดงพิสัยของค่าจำนวนเต็มของข้อมูลแต่ละชนิด ซึ่งทำให้เราสามารถคำนวณหาค่าขนาดของข้อมูลในแต่ละชนิดได้
ตัวอย่าง 2.3 พิสัยข้อมูลชนิดจำนวนเต็ม
#include <iostream> #include <limits> using namespace std; int main ( ) { //prints some of constants in limits header cout << "minimum short = " << SHRT_MIN << endl; cout << "maximum short = " << SHRT_MAX << endl; cout << "minimum unsigned short = 0" << endl; cout << "maximum unsigned short = " << USHRT_MAX << endl; cout << "minimum int = " << INT_MIN << endl; cout << "maximum int = " << INT_MAX << endl; cout << "minimum unsigned int = 0" << endl; cout << "maximum unsigned int = " << UINT_MAX << endl; cout << "minimum long = " << LONG_MIN << endl; cout << "maximum long = " << LONG_MAX << endl; cout << "minimum unsigned long = 0" << endl; cout << "maximum unsigned long = " << ULONG_MAX << endl; }
ทดสอบโปรแกรม
minimum short = -32768 maximum short = 32767 minimum unsigned short = 0 maximum unsigned short = 65535 minimum int = -2147483648 maximum int = 2147483647 minimum unsigned int = 0 maximum unsigned int = 4294967295 minimum long = -2147483648 maximum long = 2147483647 minimum unsigned long = 0 maximum unsigned long = 4294967295
ตัวคงที่ในโปรแกรมข้างต้น เช่น SHRT_MIN ถูกนิยามไว้ในไฟล์ส่วนหัวที่ชื่อ <limits> ซึ่งเป็นคลังโปรแกรมที่เก็บตัวคงที่ของพิสัยข้อมูลจำนวนเต็ม เช่น จากตัวอย่างค่าจำนวนเต็มต่ำสุดที่ข้อมูลชนิด int สามารถเก็บค่าได้ คือ -2,147,483,648 และ ค่าจำนวนเต็มสูงสุดที่ข้อมูลชนิด int สามารถเก็บค่าได้ คือ 2,147,483,647 จากพิสัยดังกล่าวแสดงให้เห็นว่าข้อมูลชนิด int มีขนาด 4 ไบต์ พิสัยและขนาดของข้อมูลชนิดจำนวนเต็มสามารถสรุปได้ตามตาราง 2-1
ตาราง 2-1 ขอบเขตและขนาดของชนิดจำนวนเต็ม
ชนิด | ค่าต่ำสุด | ค่าสูงสุด | ขนาด (ไบต์) |
short | -32,768 | 32,767 | 2 |
int | -2,147,483,648 | 2,147,483,647 | 4 |
long | -2,147,483,648 | 2,147,483,647 | 4 |
unsigned short | 0 | 65,535 | 2 |
unsigned int | 0 | 4,294,967,295 | 4 |
unsigned long | 0 | 4,294,967,295 | 4 |
หมายเหตุ โปรแกรมตัวอย่างแปลรหัสคำสั่งด้วยโปรแกรม g++ สำหรับเครื่องคอมพิวเตอร์ส่วนบุคคลที่มีซีพียูเป็น Intel x64 บนระบบปฏิบัติการ Windows 2010
จากตาราง จะเห็นว่าข้อมูลชนิด short และ unsigned short สำรองเนื้อที่ขนาด 2 ไบต์ในการเก็บข้อมูล ในขณะที่ข้อมูลชนิด int, unsigned int, long และ unsigned long สำรองเนื้อที่ขนาด 4 ไบต์ในการเก็บข้อมูล และ ข้อมูลชนิด int เหมือนกับข้อมูลชนิด long และ ข้อมูลชนิด unsigned int เหมือนกับข้อมูลชนิด unsigned long
ภาษา C++ มีตัวดำเนินการทางคณิตศาสตร์ (arithmetic operators) ทั้งสิ้น 5 ตัว ที่กระทำกับวัตถุที่มีข้อมูลชนิดเลขจำนวนเต็ม คือ ตัวดำเนินการบวก ตัวดำเนินการลบ ตัวดำเนินการคูณ ตัวดำเนินการผลหารจำนวนเต็ม และ ตัวดำเนินการเศษจากการหารจำนวนเต็ม โดยใช้เครื่องหมาย +, -, *, / และ % ตามลำดับ
ตัวอย่าง 2.4 ตัวดำเนินการคณิตศาสตร์
int main ( ) { // test operators +, -, *, / and % int m = 54; int n = 20; cout << "m = " << m << " and n = " << n << endl; cout << "m+n = " << m + n << endl; // 54+20 = 74 cout << "m-n = " << m - n << endl; // 54-20 = 34 cout << "m*n = " << m * n << endl; // 54*20 = 1080 cout << "m/n = " << m / n << endl; // 54/20 = 2 cout << "m%n = " << m % n << endl; // 54%20 = 14 }
ทดสอบโปรแกรม
m = 54 and n = 20 m+n = 74 m-n = 34 m*n = 1080 m/n = 2 m%n = 14
จากตัวอย่าง ตัวดำเนินการ / ที่กระทำกับข้อมูลชนิดเลขจำนวนเต็ม จะได้ค่าผลหารเป็นจำนวนเต็ม เช่น 54 / 20 ได้ 2 ไม่ใช่ 2.7 ส่วนตัวดำเนินการ % กระทำการหารจำนวนเต็ม แล้วเก็บค่าจำนวนเต็มที่เป็นเศษเหลือจากการหาร เช่น 54 % 20 ได้ 14
ค่าของข้อมูลชนิดเลขจำนวนเต็มสามารถเพิ่ม หรือ ลดค่าไป 1 ด้วยการใช้ตัวดำเนินการส่วนเพิ่ม ++ (increment operator) และ ตัวดำเนินการส่วนลด – (decrement operator) ตามลำดับ ตัวดำเนินการ ++ และ – สามารถเป็นได้ทั้งแบบกระทำก่อนตัวดำเนินการอื่น (pre) และ แบบกระทำหลังตัวดำเนินการอื่น (post) โดยวางตัวดำเนินการ ++ และ – ไว้หน้าหรือหลังตัวถูกกระทำการ
ตัวอย่าง 2.5 การใช้งานตัวดำเนินการส่วนเพิ่มและส่วนลด
int main ( ) { // show the different between m++ and ++m int m, n; m = 44; n = ++m; cout << "m = " << m << " and n = " << n << endl; m = 44; n = m++; cout << "m = " << m << " and n = " << n << endl; }
ทดสอบโปรแกรม
m = 45 and n = 45 m = 45 and n = 44
คำสั่ง
n = ++m;
ตัวดำเนินการส่วนเพิ่ม ++ อยู่หน้าตัวแปร m เป็นแบบกระทำก่อน ดังนั้น ค่าในตัวแปร m จะถูกเพิ่มค่าไป 1 เป็น 45 ก่อนจะนำไปเก็บไว้ในตัวแปร n ทำให้ตัวแปร n มีค่าเป็น 45
ส่วนคำสั่ง
n = m++;
ตัวดำเนินการส่วนเพิ่ม ++ อยู่หลังตัวแปร m เป็นแบบกระทำทีหลัง ดังนั้น ค่าในตัวแปร m จะถูกนำไปเก็บไว้ในตัวแปร n ทำให้ตัวแปร n มีค่าเป็น 44 แล้วจึงเพิ่มค่าในตัวแปร m ไป 1 เป็น 45
ในภาษา C++ มีตัวดำเนินการที่เป็นทั้งตัวดำเนินการคณิตศาสตร์ และ ตัวดำเนินการกำหนดค่า เรียกว่า ตัวดำเนินการกำหนดค่ารวม (Composite Assignment Operators) เช่น += , -=, *=, /= และ %= ตัวดำเนินการดังกล่าวจะกระทำต่อวัตถุที่เป็นตัวแปรทางซ้ายของตัวดำเนินการ ด้วยการกระทำทางคณิตศาสตร์กับนิพจน์ทางขวาของตัวดำเนินการ แล้วนำผลลัพธ์ที่ได้ไปกำหนดค่าให้กับตัวแปรทางซ้าย
ตัวอย่าง 2.6 การใช้งานตัวดำเนินการคณิตศาสตร์รวม
int main ( ) { int n = 22; cout << "n = " << n << endl; n += 9; cout << "n += 9 = " << n << endl; n -= 5; cout << "n -= 5 = " << n << endl; n *= 2; cout << "n *= 2 = " << n << endl; n /= 3; cout << "n /= 3 = " << n << endl; n %= 7; cout << "n %= 7 = " << n << endl; }
ทดสอบโปรแกรม
n = 22 n += 9 = 31 n -= 5 = 26 n *= 2 = 52 n /= 3 = 17 n %= 7 = 3
ตัวดำเนินการเปรียบเทียบ (comparison operators) คือ ตัวดำเนินการที่ใช้ในการเปรียบเทียบค่านิพจน์ของข้อมูล ผลลัพธ์ที่ได้จากการเปรียบเทียบจะเป็นค่านิพจน์จำนวนเต็ม ที่สามารถใช้เป็นเงื่อนไขในการตรวจสอบ โดยจะให้ค่าเป็นจำนวนเต็ม 1 เมื่อผลการเปรียบเทียบมีค่าเป็นจริง (true) และ จำนวนเต็ม 0 เมื่อผลการเปรียบเทียบมีค่าเป็นเท็จ (false) ในภาษา C++ มีตัวดำเนินการเปรียบเทียบ อยู่ 6 ตัว ดังนี้
x < y // ให้ผลลัพธ์เป็น จริง (1) เมื่อ x มี ค่าน้อยกว่า y x > y // ให้ผลลัพธ์เป็น จริง (1) เมื่อ x มี ค่ามากกว่า y x <= y // ให้ผลลัพธ์เป็น จริง (1) เมื่อ x มี ค่าน้อยกว่า หรือ เท่ากับ y x >= y // ให้ผลลัพธ์เป็น จริง (1) เมื่อ x มี ค่ามากกว่า หรือ เท่ากับ y x == y // ให้ผลลัพธ์เป็น จริง (1) เมื่อ x มี ค่าเท่ากับ y x != y // ให้ผลลัพธ์เป็น จริง (1) เมื่อ x มี ค่าไม่เท่ากับ y
เช่น 7 * 8 < 6 * 9 จะให้ผลลัพธ์เป็นจำนวนเต็ม 0 เนื่องจาก ผลคูณของ 7 * 8 มีค่าเท่ากับ 56 ซึ่งมีค่ามากกว่าผลคูณ 6 * 9 ที่มีค่าเป็น 54
ตัวอย่าง 2.7 แสดงผลการทำงานของตัวดำเนินการเปรียบเทียบ
int main() { // prints results of comparison operators int i = 3, j = -5; cout << "i < 5 \tis " << (i < 5) << endl; // 3 < 5 is true cout << "i+j > 0 \tis " << (i+j > 0) << endl; // -2 > 0 is false cout << "i-j <= i+j \tis " << (i-j <= i+j) << endl; // 8 <= -2 is false cout << "2*i >= 2+i \tis " << (2*i >= 2+i) << endl; // 6 >= 5 is true cout << "i-3 == j+5 \tis " << (i-3 == j+5) << endl; // 0 == 0 is true cout << "i%2 != j+6 \tis " << (i%2 != j+6) << endl; // 1 != 1 is false }
ข้อสังเกต การเขียนนิพจน์เงื่อนไขในภาษา C++ จะเขียนภายในเครื่องหมายวงเล็บเสมอ
ทดสอบโปรแกรม
i < 5 is 1 i+j > 0 is 0 i-j <= i+j is 0 2*i >= 2+i is 1 i-3 == j+5 is 1 i%2 != j+6 is 0
สิ่งที่ผู้เขียนโปรแกรมควรระลึกไว้เสมอในการเขียนโปรแกรมภาษา C++ คือ ในภาษา C++ เครื่องหมาย = หมายถึง ตัวดำเนินการกำหนดค่า ในขณะที่ เครื่องหมาย == หมายถึง ตัวดำเนินการเปรียบเทียบการเท่ากัน เช่น คำสั่ง
x = 33;
จะนำค่า 33 ใส่ในตัวแปร x ผลที่ได้ x มีค่าเป็น 33 ส่วนนิพจน์
x == 33
จะนำค่า x เปรียบเทียบกับ 33 ว่าเท่ากันหรือไม่ ผลที่ได้เป็นจริง (จำนวนเต็ม 1) เมื่อ x มีค่าเป็น 33 หรือ ได้ค่าเป็นเท็จ (จำนวนเต็ม 0) เมื่อ x มีค่าอื่นที่ไม่ใช่ 33 โดยที่ หลังการทำงาน x จะยังคงมีค่าเท่าเดิม
ตัวดำเนินการตรรกะ (logical operators) คือ ตัวดำเนินการที่ใช้เชื่อมนิพจน์จำนวนเต็มหรือนิพจน์เงื่อนไข เช่น n%d และ x >= y เพื่อสร้างเงื่อนไขเชิงซ้อน (compound condition) ในภาษา C++ มีตัวดำเนินการตรรกะอยู่ 3 ตัว คือ && (and), || (or) และ ! (not) โดยที่ p && q จะได้ค่าเป็นจริง ก็ต่อเมื่อทั้งนิพจน์ p และ q มีค่าเป็นจริงทั้งคู่ P || q จะได้ค่าเป็นเท็จ ก็ต่อเมื่อทั้งนิพจน์ p และ q มีค่าเป็นเท็จทั้งคู่ !p จะได้ค่าเป็นค่าตรงข้ามกับค่าของนิพจน์ p
เช่น (n%d || x >= y) จะให้ค่าเป็นเท็จ ถ้าเพียงแต่ n%d มีค่าเป็นศูนย์ และ x มีค่าน้อยกว่า y ตารางค่าความจริง (truth table) ของนิยามที่ได้จากตัวดำเนินการตรรกะทั้งสามตัว สามารถแสดงได้ดังตาราง 2-2
ตาราง 2-2 ตารางค่าความจริงของตัวดำเนินการตรรกะ
p | q | p && q | p||q |
T | T | T | T |
T | F | F | T |
F | T | F | T |
F | F | F | F |
p | !p |
T | F |
F | T |
หมายเหตุ T หมายถึง ค่าที่เป็นจริง และ F หมายถึง ค่าที่เป็นเท็จ
ตัวอย่าง 2.8 แสดงผลการทำงานของตัวดำเนินการตรรกะ
int main() { // prints results of logical operators int n = 10; cout << "n > 0 && n < 20 is " << (n > 0 && n < 20) << endl; // T && T is T cout << "n > 0 && n < 5 is " << (n > 0 && n < 5 ) << endl; // T && F is F cout << "n > 0 || n < 5 is " << (n > 0 || n < 5 ) << endl; // T || F is T cout << "n < 0 || n > 20 is " << (n < 0 || n > 20) << endl; // F || F is F cout << "!(n == 5) is " << (!(n == 5)) << endl; // ! F is T }
ข้อสังเกต ตัวดำเนินการเปรียบเทียบจะถูกประมวลผลก่อนตัวดำเนินการตรรกะ ยกเว้น ตัวดำเนินการ ! (not) จะถูกกระทำก่อนตัวดำเนินการเปรียบเทียบ เราจึงต้องใส่เครื่องหมายวงเล็บคร่อมนิพจน์เงื่อนไข (n == 5) เพื่อให้โปรแกรมทำการเปรียบเทียบก่อน
ทดสอบโปรแกรม
n > 0 && n < 20 is 1 n > 0 && n < 5 is 0 n > 0 || n < 5 is 1 n < 0 || n > 20 is 0 !(n == 5) is 1
ข้อมูลชนิดจุดลอยตัว (floating-point types) ในภาษา C++ เป็นชนิดข้อมูลที่รองรับการเก็บค่าจำนวนจริง (real numbers) มีด้วยกัน 3 ชนิด คือ ชนิด float ชนิด double และ ชนิด long double โดยที่ชนิด float จะใช้เนื้อที่ 4 ไบต์ในการเก็บข้อมูล ชนิด double จะใช้เนื้อที่ 8 ไบต์ในการเก็บข้อมูล และ ชนิด long double จะใช้เนื้อที่ 8 ไบต์ในการเก็บข้อมูล (ขนาดของข้อมูลอาจจะแตกต่างกัน ขึ้นกับระบบคอมพิวเตอร์ เช่น ในบางระบบคอมพิวเตอร์ ข้อมูลชนิด float ชนิด double และ ชนิด long double อาจมีขนาดเป็น 10, 12 และ 16 ไบต์ ก็ได้)
ข้อมูลชนิด float มีการจัดแบ่งโครงสร้าง 32 บิตข้อมูล โดยใช้เนื้อที่ขนาด 1 บิต ในการเก็บเครื่องหมาย (sign) ใช้เนื้อที่ขนาด 23 บิต ในการเก็บค่าแมนทิสซา (mantissa) และ ใช้เนื้อที่ขนาด 8 บิต ในการเก็บค่าเลขชี้กำลัง (exponent) ในขณะที่ ข้อมูลชนิด double มีการจัดแบ่งโครงสร้าง 64 บิตข้อมูล โดยใช้เนื้อที่ขนาด 1 บิต ในการเก็บเครื่องหมาย ใช้เนื้อที่ขนาด 52 บิต ในการเก็บค่าแมนทิสซา และ ใช้เนื้อที่ขนาด 11 บิต ในการเก็บค่าเลขชี้กำลัง
ข้อมูลชนิดจุดลอยตัวในภาษา C++ มีตัวดำเนินการคณิตศาสตร์ไว้ใช้งานอยู่ 4 ตัว คือ ตัวดำเนินการบวก ตัวดำเนินการลบ ตัวดำเนินการคูณ และ ตัวดำเนินการหารแบบค่าเฉลี่ย โดยใช้เครื่องหมาย +, -, * และ / เช่นเดียวกับตัวดำเนินการคณิตศาสตร์ของชนิดเลขจำนวนเต็ม
ตัวอย่าง 2.9 การใช้งานตัวดำเนินการคณิตศาสตร์สำหรับชนิดจุดลอยตัว
int main ( ) { // test the floating point operators +, -, * and / double x = 54; double y = 20; cout << "x = " << x << " and y = " << y << endl; cout << "x+y = " << x + y << endl; cout << "x*y = " << x * y << endl; cout << "x/y = " << x / y << endl; }
ทดสอบโปรแกรม
x = 54 and y = 20 x+y = 74 x-y = 34 x*y = 1080 x/y = 2.7
ข้อสังเกตุ ตัวดำเนินการ / สำหรับชนิดจุดลอยตัว จะให้ผลลัพธ์เป็นข้อมูลชนิดจุดลอยตัว ซึ่งต่างจากการใช้ตัวดำเนินการ / ของข้อมูลชนิดเลขจำนวนเต็ม
ตัวอย่างต่อไป จะแสดงขนาดของข้อมูลชนิดต่างๆ ในภาษา C++ มาตรฐาน โดยใช้ตัวดำเนินการ sizeof ซึ่งเป็นจะทำการส่งค่าขนาดข้อมูลคืนกลับ เมื่อผู้เขียนโปรแกรมส่งชนิดข้อมูลเป็นพารามิเตอร์
ตัวอย่าง 2.10 การใช้งานตัวดำเนินการ sizeof
int main() { // prints the storage sizes of the fundamental types cout << "Number of bytes used: \n"; cout << "bool : " << sizeof(bool) << endl; cout << "char : " << sizeof(char) << endl; cout << "short : " << sizeof(short) << endl; cout << "int : " << sizeof(int) << endl; cout << "long : " << sizeof(long) << endl; cout << "unsigned short : " << sizeof(unsigned short) << endl; cout << "unsigned int : " << sizeof(unsigned int) << endl; cout << "unsigned long : " << sizeof(unsigned long) << endl; cout << "float : " << sizeof(float) << endl; cout << "double : " << sizeof(double) << endl; cout << " long double : " << sizeof(long double) << endl; }
ทดสอบโปรแกรม
Number of bytes used: bool : 1 char : 1 short : 2 int : 4 long : 4 unsigned short : 2 unsigned int : 4 unsigned long : 4 float : 4 double : 8 long double : 10
ตัวอย่างต่อไป แสดงโครงสร้างข้อมูลชนิด float โดยการแสดงค่าที่จัดเก็บในตัวคงที่ FLT_ DIG, FLT_MAN_DIG, FLT_MIN และ FLT_MAX ที่ถูกนิยามไว้ในไฟล์ส่วนหัว <cfloat>
ตัวอย่าง 2.11 การแสดงค่าตัวคงที่ในไฟล์ส่วนหัว <cfloat>
#include <cfloat> #include <iostream> using namespace std; int main() { // prints the storage sizes of the fundamental types int fbits = 8 * sizeof(float); cout << "float uses " << fbits << " bits:\n\t " << FLT_MANT_DIG - 1 << " bits for its mantissa,\n\t " << fbits - FLT_MANT_DIG << " bits for its exponent,\n\t " << 1 << " bits for sign\n" << " to obtain: " << FLT_DIG << "sig.digits\n" << " with minimum value: " << FLT_MIN << endl << " and maximum value: " << FLT_MAX << endl; }
ทดสอบโปรแกรม
float uses 32 bits: 23 bits for its mantissa, 8 bits for its exponent, 1 bits for sign to obtain: 6sig.digits with minimum value: 1.17549e-38 and maximum value: 3.40282e+38
ผลรันที่ได้จากตัวอย่าง แสดงขนาดของโครงสร้างข้อมูลชนิด float ว่ามีเนื้อที่ขนาด 4 ไบต์ ประกอบด้วย เนื้อที่ขนาด 23 บิตในการเก็บค่าแมนทิสซา เนื้อที่ขนาด 8 บิตในการเก็บค่าเลขชี้กำลัง และ เนื้อที่ขนาด1 บิตในการเก็บเครื่องหมาย โดยสามารถแสดงนัยสำคัญได้ 6 ตำแหน่ง และ เก็บค่าจำนวนจริงได้ภายในช่วง 1.17549×10-38 ถึง 3.40282×1038
ในภาษา C++ ตัวดำเนินการคณิตศาสตร์ที่กระทำกับข้อมูลชนิดเลขจำนวนเต็มกับชนิดจุดลอยตัว จะมีการแปลงค่าข้อมูลชนิดเลขจำนวนเต็ม ไปเป็นข้อมูลชนิดจุดลอยตัวโดยอัตโนมัติ ก่อนที่จะกระทำการคำนวณทางคณิตศาสตร์ เช่น
int n = 22; float x = 3.14159; x += n; cout << x – 2 << endl;
คำสั่งในบรรทัดที่ 3 ค่าจำนวนเต็ม 22 ในตัวแปร n จะถูกเปลี่ยนเป็น 22.0 โดยอัตโนมัติ ก่อนนำไปบวกเพิ่มกับค่าจำนวนจริง 3.14.159 ในตัวแปร x ในทำนองเดียวกัน ค่าคงที่จำนวนเต็ม 2 ของคำสั่งในบรรทัดที่ 4 จะถูกเปลี่ยนเป็น 2.0 ก่อนที่จะดำเนินการลบกับค่าในตัวแปร x แล้วนำผลลัพธ์ที่ได้ไปแสดงผลทางจอภาพ
โดยทั่วไป ในภาษา C++ ผู้เขียนโปรแกรมสามารถแปลงชนิดข้อมูล ไปเป็นชนิดข้อมูลรูปแบบอื่นได้ โดยทำการแคสชนิดข้อมูล (type casting) ตามรูปแบบ
T (v)
เมื่อ T คือ ชนิดข้อมูลใหม่ที่ต้องการให้เป็น และ v คือ ตัวแปรที่เก็บค่าข้อมูลเดิม เช่น
float y = 2.71828; int n; n = int(y);
การแคสชนิดข้อมูลในบรรทัดที่ 3 จะมีผลให้ตัวแปร n มีค่าเป็นจำนวนเต็ม 2 เนื่องจาก ค่าของตัวแปร y ซึ่งมีค่าเป็นจำนวนจริง 2.71828 จะถูกนำมาเปลี่ยนเป็นค่าจำนวนเต็ม ด้วยการตัดเศษทิ้ง (truncate) ได้ค่าเป็นจำนวนเต็ม 2 ก่อนจะนำไปเก็บในตัวแปร n โดยที่ ตัวแปร y ยังคงเก็บค่าจำนวนจริง และมีค่าเป็น 2.71828 เช่นเดิม
ในภาษา C รูปแบบคำสั่งการแคสชนิดข้อมูล จะเขียนแตกต่างกับภาษา C++ คือ (T) v รูปแบบดังกล่าวสามารถใช้ได้ในโปรแกรมภาษา C++ เช่นกัน เช่น
n = (int) y;
ตัวอย่าง 2.12 แสดงการแคสชนิดข้อมูล
int main() { // casts a double value as an int double v = 1234.5678; int n = int(v); cout << "v = " << v << ", n = " << n << endl; }
ทดสอบโปรแกรม
v = 1234.57, n = 1234
ในภาษา C++ การเปลี่ยนชนิดข้อมูลไปเป็นรูปแบบชนิดที่สูงขึ้น เช่น เปลี่ยนจากชนิด char เป็นชนิด int หรือ เปลี่ยนจากชนิด int เป็นชนิด float การเปลี่ยนแปลงดังกล่าวจะเกิดขึ้นโดยอัตโนมัติ เรียกว่า การโปรโมตชนิดข้อมูล (type promotion)
ตัวอย่าง 2.13 แสดงการโปรโมตชนิดข้อมูล
#include <iostream> using namespace std; int main() { // prints promoted values 65 from char to double char c = 'A'; cout << " char c = " << c << endl; short k = c; cout << " short k = " << k << endl; int m = k; cout << " int m = " << m << endl; long n = m; cout << " long n = " << n << endl; float x = n; cout << " float x = " << x << endl; double y = x; cout << "double y = " << y << endl; }
ทดสอบโปรแกรม
char c = A short k = 65 int m = 65 long n = 65 float x = 65 double y = 65
อักขระ ‘A’ ตามรหัสแอสกี มีค่าเป็นจำนวนเต็ม 65 จะถูกเปลี่ยนเป็นชนิด char แล้วเก็บในตัวแปร c ค่าจำนวนเต็ม 65 ในตัวแปร c จะถูกเปลี่ยนเป็นชนิด short แล้วเก็บในตัวแปร k ค่าจำนวนเต็ม 65 ในตัวแปร k จะถูกเปลี่ยนเป็นชนิด int แล้วเก็บในตัวแปร m ค่าจำนวนเต็ม 65 ในตัวแปร m จะถูกเปลี่ยนเป็นชนิด long แล้วเก็บในตัวแปร n ค่าจำนวนเต็ม 65 ในตัวแปร n จะถูกเปลี่ยนเป็น 65.0 ชนิด float แล้วเก็บในตัวแปร x สุดท้าย ค่าจำนวนจริง 65.0 ในตัวแปร x จะถูกเปลี่ยนเป็นชนิด double แล้วเก็บในตัวแปร y จากผลรันของโปรแกรมตัวอย่างจะเห็นว่า การแสดงผลข้อมูลชนิดจุดลอยตัวที่ไม่มีส่วนเศษ ในภาษา C++ จะแสดงผลเฉพาะส่วนจำนวนเต็มเท่านั้น
ปัญหาการล้นของข้อมูลเลขจำนวน (numeric overflow) เป็นปัญหาในการคำนวณที่เกิดขึ้น เมื่อผลการคำนวณมีค่ามากกว่าค่าสูงสุดที่ข้อมูลชนิดนั้นสามารถเก็บได้ หรือ มีค่าน้อยกว่าค่าต่ำสุดที่ข้อมูลชนิดนั้นสามารถเก็บได้ ปัญหาการล้นของข้อมูลบางครั้ง อาจไม่ฟ้องข้อผิดพลาดในการประมวลผลคำสั่งนั้น ๆ แต่มีผลให้ตัวแปรเก็บค่าที่ไม่ถูกต้อง ผู้เขียนโปรแกรมจะต้องตระหนักถึงปัญหาดังกล่าว เมื่อมีการทำงานกับข้อมูลที่มีค่าสัมบูรณ์มากๆ
ตัวอย่าง 2.14 การล้นของข้อมูลจำนวนเต็ม
int main ( ) { //prints n until it overflows int n = 1000; cout << "n = " << n << endl; n *= 1000; // multiply n by 1000 cout << "n = " << n << endl; n *= 1000; // multiply n by 1000 cout << "n = " << n << endl; n *= 1000; // multiply n by 1000 cout << "n = " << n << endl; }
ทดสอบโปรแกรม
n = 1000 n = 1000000 n = 1000000000 n = -727379968
ในผลลัพธ์สุดท้ายค่าของตัวแปร n เป็น –727,379,968 แทนที่จะได้ 1012 เนื่องจากค่า 1012 มีค่ามากกว่าค่าสูงสุดที่จำนวนเต็มสามารถเก็บค่าได้ (2,147,483,647) ทำให้ค่าของจำนวนเต็มย้อนกลับไปเป็นค่าจำนวนเต็มลบ เกิดปัญหาการวนรอบ (wrap around)
ตัวอย่าง 2.15 การล้นของข้อมูลจำนวนจริง
int main ( ) { //prints x until it overflows float x = 1000.0; cout << "x = " << x << endl; x *= x; // multiply x by itself cout << "x = " << x << endl; x *= x; // multiply x by itself cout << "x = " << x << endl; x *= x; // multiply x by itself cout << "x = " << x << endl; x *= x; // multiply x by itself cout << "x = " << x << endl; }
ทดสอบโปรแกรม
x = 1000 x = 1e+06 x = 1e+12 x = 1e+24 x = inf
หมายเหตุ inf มาจาก infinity หมายถึง ค่าอนันต์
ค่าของตัวแปร x ในคำสั่งสุดท้ายมีค่าเป็น 1×1048 ซึ่งเป็นค่าจำนวนจริงที่มากกว่าค่าสูงสุดที่ข้อมูลชนิด float สามารถเก็บค่าได้ (3.40282×1038) ทำให้ไม่สามารถแทนค่าได้ตามโครงสร้างข้อมูล เกิดปัญหาการจมของข้อมูล (sink) ซึ่งแตกต่างกับปัญหาการล้นของข้อมูลชนิดจำนวนเต็ม
ข้อผิดพลาดการปัดเศษ (round-off error) เป็นปัญหาหนึ่งที่พบในการดำเนินการทางคณิตศาสตร์กับตัวถูกกระทำชนิดจุดลอยตัว เช่น 1/3 อาจมีค่าเป็น 0.333333 ซึ่งไม่ใช่ค่าที่เท่ากันพอดีกับค่า 1/3 จริงๆ การเก็บค่าโดยประมาณของข้อมูลชนิดจุดลอยตัว ทำให้เกิดส่วนต่างของค่าโดยประมาณที่จัดเก็บกับค่าจริงที่ควรจะเป็น ก่อให้เกิดข้อผิดพลาดการปัดเศษขึ้น บางครั้งข้อผิดพลาดดังกล่าวอาจส่งผลร้ายแรงต่อการทำงานของโปรแกรมได้ ผู้เขียนโปรแกรมจึงควรพิจารณาปัญหาดังกล่าวอย่างถี่ถ้วน เช่นเดียวกับปัญหาการล้นของข้อมูลเลขจำนวน
ตัวอย่าง 2.16 ปัญหาการปัดเศษ
int main ( ) { //illustrates round-off error double x = 1000/3.0; cout << "x = " << x << endl; //x = 1000/3 double y = x - 333.0; cout << "y = " << y << endl; //y = 1/3 double z = 3*y - 1.0; cout << "z = " << z << endl; //z = 3(1/3)-1 if (z == 0) cout << "z = 0.\n" ; else cout << "z does not equal 0.\n"; }
ทดสอบโปรแกรม
x = 333.333 y = 0.333333 z = -5.68434e-14 z does not equal 0.
ในทางคณิตศาสตร์ ค่าของตัวแปร x ควรมีค่าเป็น 333 1/3 แต่ค่าของตัวแปร y ควรมีค่าเป็น 1/3 และ ค่าของตัวแปร z ควรมีค่าเป็น 0 แต่เนื่องจากค่า 1/3 ไม่สามารถแทนค่าที่พอดีได้ในรูปของจำนวนจริง ส่วนต่างในการแทนค่าก่อให้เกิดข้อผิดพลาดขึ้น ผลที่ตามมา คือ ค่าในตัวแปร z มีค่าไม่เท่ากับ 0 ทำให้ผลการเปรียบเทียบค่าในตัวแปร z กับ 0 ได้ผลเป็นเท็จ คำสั่ง if..else สุดท้าย จึงแสดงข้อความ z does not equal 0. ทางจอภาพ จากปัญหาการเก็บค่าประมาณของจำนวนจริง ผู้เขียนโปรแกรมจึงไม่ควรใช้การเปรียบเทียบเท่ากัน (==) กับข้อมูลชนิดจุดลอยตัว
ตัวอย่าง 2.17 ปัญหาการปัดเศษ
#include <cmath> #include <iostream> using namespace std; int main ( ) { //implement the quadratic formula float a, b, c; cout << "Enter the coefficients of a quadratic equation: " << endl; cout << "\ta: "; cin >> a; cout << "\tb: "; cin >> b; cout << "\tc: "; cin >> c; cout << "The equation is: " << a << "*x*x + " << b << "*x + " << c << " = 0" << endl; float d = b*b - 4*a*c; //discriminant float sqrtd = sqrt(d); float x1 = (-b + sqrtd) / (2*a); float x2 = (-b - sqrtd) / (2*a); cout << "The solution are:" << endl; cout << "\tx1 = " << x1 << endl; cout << "\tx2 = " << x2 << endl; cout << "Check:" << endl; cout << "\ta*x1*x1 + b*x1 + c = " << a*x1*x1 + b*x1 + c << endl; cout << "\ta*x2*x2 + b*x2 + c = " << a*x2*x2 + b*x2 + c << endl; }
ทดสอบโปรแกรม โดยป้อนค่า a = 2, b = 1 และ c = -3
Enter the coefficients of a quadratic equation: a: 2 b: 1 c: -3 The equation is: 2*x*x + 1*x + -3 = 0 The solution are: x1 = 1 x2 = -1.5 Check: a*x1*x1 + b*x1 + c = 0 a*x2*x2 + b*x2 + c = 0
ทดสอบโปรแกรมอีกครั้ง โดยป้อนค่า a = 2, b = 8.001 และ c = 8.002
Enter the coefficients of a quadratic equation: a: 2 b: 8.001 c: 8.002 The equation is: 2*x*x + 8.001*x + 8.002 = 0 The solution are: x1 = -1.99952 x2 = -2.00098 Check: a*x1*x1 + b*x1 + c = 5.35749e-11 a*x2*x2 + b*x2 + c = -2.96609e-10
จะเห็นว่าในการประมวลผลครั้งที่ 2 เมื่อนำค่าตัวแปร x1 และ x2 ไปแทนค่าในสมการ 2x^2 + 8.001x + 8.002 จะได้ค่าที่ไม่เป็นศูนย์ อันเนื่องจากปัญหาการปัดเศษ
ปัญหาการล้นของข้อมูลเลขจำนวน และ ปัญหาการปัดเศษ เป็นตัวอย่างข้อผิดพลาดที่พบในช่วงของการประมวลผล (run-time errors) ซึ่งส่วนมากจะส่งผลที่รุนแรงกว่าข้อผิดพลาดที่พบในช่วงของการแปลโปรแกรม (compile-time errors) เนื่องจาก ข้อผิดพลาดในช่วงการประมวลผลยากที่จะตรวจสอบ และค้นหาที่ผิด ซึ่งแตกต่างจากข้อผิดพลาดในช่วงการแปลโปรแกรม ที่โปรแกรมแปลโปรแกรมจะช่วยตรวจหาข้อผิดพลาด ในขณะที่กำลังแปลคำสั่งในโปรแกรม ปัญหาในช่วงการประมวลผลในด้านการคำนวณอาจเกิดขึ้นจากการที่ไม่สามารถหาผลการคำนวณทางคณิตศาสตร์นั้น ๆ ได้ เช่น การหาค่ารากที่สองของค่าที่เป็นลบ
ทดสอบโปรแกรม โดยป้อนค่า a = 1, b = 2 และ c = 3
Enter the coeffecients of a quadratic equation: a: 1 b: 2 c: 3 The equation is: 1*x*x + 2*x + 3 = 0 The solution are: x1 = nan x2 = nan Check: a*x1*x1 + b*x1 + c = nan a*x2*x2 + b*x2 + c = nan
หมายเหตุ nan มาจาก not a number หมายถึงไม่สามารถแทนค่าเป็นเลขจำนวนได้
จากตัวอย่าง ค่า b2 – 4ac มีค่าเท่ากับ -8 เมื่อส่งค่า -8 ไปหาค่ารากที่สองในฟังก์ชัน sqrt( ) ทำให้ไม่สามารถหาค่าได้ จึงก่อให้เกิดข้อผิดพลาดในการประมวลผล ปัญหาการหารด้วยค่าที่เป็น 0 เป็นอีกปัญหาหนึ่งที่พบในช่วงการประมวลผล
ทดสอบโปรแกรม โดยป้อนค่า a = 0, b = 2 และ c = 5
Enter the coeffecients of a quadratic equation: a: 0 b: 2 c: 5 The equation is: 0*x*x + 2*x + 5 = 0 The solution are: x1 = nan x2 = -inf Check: a*x1*x1 + b*x1 + c = nan a*x2*x2 + b*x2 + c = nan
จากตัวอย่าง ตัวแปร a มีค่าเป็น 0 เมื่อทำการหารด้วย a จึงเกิดข้อผิดพลาดขึ้น
ในภาษา C++ สามารถเขียนค่าของข้อมูลจุดลอยตัวได้ 2 แบบ คือ แบบจุดตรึง (fixed-point) และ แบบเชิงวิทยาศาสตร์ (scientific) เช่น 333.333 เป็นการแทนค่าแบบจุดตรึง และ -5.68434e-14 เป็นการแทนค่าแบบเชิงวิทยาศาสตร์ โดย e หรือ E หมายถึง เลขชี้กำลังของ 10 เช่น e-14 หมายถึง 10-14 รูปแบบ เชิงวิทยาศาสตร์เหมาะกับการแสดงผลค่าตัวเลขที่ต่ำหรือสูงมากๆ โดยทั่วไป ค่าจุดลอยตัวที่อยู่ระหว่าง 0.000001 ถึง 999,999 จะแสดงผลในรูปแบบจุดตรึง และ ค่าที่นอกเหนือช่วงดังกล่าวจะแสดงผลในรูปแบบเชิงวิทยาศาสตร์
ตัวอย่าง 2.18 การทำงานในรูปแบบเชิงวิทยาศาสตร์
int main ( ) { //prints double value in scientific e-format double x; cout << "Enter flaot: "; cin >> x; cout << "It's reciprocal is: " << 1/x << endl; }
ทดสอบโปรแกรม โดยป้อนค่า x = 234.567e89
Enter flaot: 234.567e89 It's reciprocal is: 4.26317e-92
หมายเหตุ ในการป้อนค่าเชิงวิทยาศาสตร์ในภาษา C++ จะใช้ตัวอักษร E หรือ e ก็ได้