User Tools

Site Tools


comp:cpp_data_type

บทที่ 2 ชนิดข้อมูลมูลฐาน (Fundamental Data Types)

ข้อมูลจัดเป็นองค์ประกอบที่สำคัญในการประมวลผลโปรแกรม คำสั่งในภาษาคอมพิวเตอร์ส่วนใหญ่เป็นคำสั่งที่ดำเนินการจัดการกับข้อมูล ในการเรียนรู้การเขียนโปรแกรม (ไม่ว่าจะใช้ภาษาคอมพิวเตอร์ใดก็ตาม) ผู้เขียนโปรแกรมจำเป็นต้องทำความเข้าใจกับข้อมูลชนิดต่างๆ ที่มีในภาษาคอมพิวเตอร์นั้น รวมถึงศึกษาวิธีการจัดการกับชนิดข้อมูลแต่ละประเภท เพื่อที่จะเขียนคำสั่งการทำงานกับข้อมูลดังกล่าวได้อย่างถูกต้อง ในบทนี้ จะกล่าวถึงชนิดข้อมูลมูลฐานชนิดต่างๆ ที่มีในภาษา C++ การประกาศข้อมูล ลักษณะการเขียนข้อมูล การเรียกใช้งานข้อมูล รวมถึง ตัวดำเนินการที่กระทำกับข้อมูลในแต่ละประเภท ในท้ายบท จะกล่าวถึง ปัญหาที่มักพบในการจัดการข้อมูลทางคอมพิวเตอร์ โดยเฉพาะข้อมูลชนิดเลขจำนวน เช่น ปัญหาการล้นของข้อมูลชนิดเลขจำนวน และ ข้อผิดพลาดการปัดเศษของข้อมูลชนิดจุดลอยตัว

2.1 ชนิดข้อมูลตัวเลข

ชนิดข้อมูลมูลฐานในภาษา 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

รูป 2-1 ชนิดข้อมูลมูลฐาน

2.2 ชนิดตรรกะ

ชนิดตรรกะ (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 ทางจอภาพ

2.3 ชนิดแจงนับ

ชนิดแจงนับ(enumeration types) เป็นชนิดข้อมูลในภาษา C++ ที่อนุญาตให้ผู้เขียนโปรแกรมสามารถกำหนดรูปแบบของชนิดข้อมูลได้เอง โดยข้อมูลดังกล่าวจะต้องประกอบด้วยค่าที่เป็นจำนวนเต็ม

2.4 ชนิดอักขระ

ชนิดอักขระ (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' ตามการแทนค่าของรหัสแอสกี

2.5 ชนิดจำนวนเต็ม

ในภาษา 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

2.6 ตัวดำเนินการคณิตศาสตร์

ภาษา 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

2.7 ตัวดำเนินการส่วนเพิ่ม และ ตัวดำเนินการส่วนลด

ค่าของข้อมูลชนิดเลขจำนวนเต็มสามารถเพิ่ม หรือ ลดค่าไป 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

2.8 ตัวดำเนินการกำหนดค่ารวม

ในภาษา 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

2.9 ตัวดำเนินการเปรียบเทียบ

ตัวดำเนินการเปรียบเทียบ (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 จะยังคงมีค่าเท่าเดิม

2.10 ตัวดำเนินการตรรกะ

ตัวดำเนินการตรรกะ (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

2.11 ชนิดจุดลอยตัว

ข้อมูลชนิดจุดลอยตัว (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

2.12 การแปลงชนิดข้อมูล

ในภาษา 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++ จะแสดงผลเฉพาะส่วนจำนวนเต็มเท่านั้น

2.13 ปัญหาการล้นของข้อมูลเลขจำนวน

ปัญหาการล้นของข้อมูลเลขจำนวน (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) ซึ่งแตกต่างกับปัญหาการล้นของข้อมูลชนิดจำนวนเต็ม

2.14 ข้อผิดพลาดการปัดเศษ

ข้อผิดพลาดการปัดเศษ (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 จึงเกิดข้อผิดพลาดขึ้น

2.15 รูปแบบ E ของข้อมูลจุดลอยตัว

ในภาษา 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 ก็ได้

comp/cpp_data_type.txt · Last modified: 2021/08/10 08:42 by wasu

Page Tools