1.4 ทศนิยมและตัวอักษร
ส่วนสูง ค่าเฉลี่ย ระยะทางเป็นกิโลเมตร พวกนี้มีทศนิยม เก็บใน int ไม่ได้ หน้านี้รู้จักชนิด double สำหรับทศนิยม เรื่องที่ต้องระวังเวลาหารเลขผสมกันระหว่างจำนวนเต็มกับทศนิยม และปิดท้ายด้วย char ชนิดที่เก็บตัวอักษรหนึ่งตัว
double เก็บทศนิยม
หาค่า BMI จากน้ำหนัก (กิโลกรัม) หารด้วยส่วนสูง (เมตร) ยกกำลังสอง ส่วนสูงมีทศนิยม จึงใช้ double
#include <stdio.h>
int main()
{
double weight, height;
scanf("%lf %lf", &weight, &height);
double bmi = weight / (height * height);
printf("%f\n", bmi);
return 0;
}
60 1.70
20.761246
scanfรับdoubleด้วย%lf(long float)printfพิมพ์doubleด้วย%fจะได้ทศนิยม 6 ตำแหน่งเสมอ ถ้าอยากได้กี่ตำแหน่งก็ได้ตามต้องการ จะเรียนในบทที่ 2- ป้อน
60ที่ไม่มีจุดก็ได้scanfแปลงเป็น 60.0 ให้เอง
หารแบบไหนได้ทศนิยม
ในหน้า 1.2 เห็นแล้วว่า 17 / 5 ได้ 3 กฎจริงคือ ถ้าทั้งสองข้างเป็นจำนวนเต็ม ผลเป็นจำนวนเต็ม ถ้ามีข้างใดข้างหนึ่งเป็นทศนิยม ผลเป็นทศนิยม ในโค้ด เลขที่มีจุดอย่าง 2.0 ถือเป็น double
#include <stdio.h>
int main()
{
printf("%d\n", 7 / 2);
printf("%f\n", 7.0 / 2);
printf("%f\n", 7 / 2.0);
printf("%f\n", 7.0 / 2.0);
return 0;
}
3
3.500000
3.500000
3.500000
บรรทัดแรกต้องพิมพ์ด้วย %d เพราะ 7 / 2 เป็น int ส่วนสามบรรทัดหลังเป็น double จึงใช้ %f ใช้ผิดตัวจะได้ค่าแปลกๆ ดูได้ในส่วนลองทำให้พัง
แปลงชนิดด้วย cast
ถ้าตัวเลขอยู่ในตัวแปร int จะเติม .0 ไม่ได้ ใช้การ แปลงชนิด (cast) แทน เขียนชื่อชนิดในวงเล็บไว้หน้าค่า เช่น (double)total คือเอาค่าของ total มาใช้เป็น double ตัวแปร total เองยังเป็น int เหมือนเดิม
#include <stdio.h>
int main()
{
int total = 245;
printf("%f\n", (double)total / 3);
printf("%f\n", (double)(total / 3));
printf("%d\n", (int)3.99);
printf("%d\n", (int)-3.99);
return 0;
}
81.666667
81.000000
3
-3
- บรรทัดที่ 6 แปลง
totalเป็นdoubleก่อน แล้วค่อยหาร จึงได้ทศนิยม - บรรทัดที่ 7 วงเล็บทำให้
total / 3ทำก่อน ได้ 81 แบบจำนวนเต็มไปแล้ว ค่อยแปลงเป็น 81.0 สายเกินไป ทศนิยมหายไปตั้งแต่ตอนหาร - แปลง
doubleเป็นintคือตัดทศนิยมทิ้งไปทาง 0 ไม่ใช่ปัดเศษ 3.99 ได้ 3 และ -3.99 ได้ -3
จำกับดัก int คูณ int ในหน้า 1.3 ได้ไหม cast แก้ได้ด้วยวิธีเดียวกัน (long long)width * length แปลง width เป็น long long ก่อนคูณ ผลคูณจึงเป็น long long และไม่ล้น
ทศนิยมไม่ได้แม่นทุกหลัก
คอมพิวเตอร์เก็บทศนิยมเป็นเลขฐานสอง เลขอย่าง 0.1 เขียนเป็นฐานสองแบบลงตัวไม่ได้ เหมือน 1/3 ที่เขียนเป็นฐานสิบได้แค่ 0.3333 ไม่รู้จบ จึงเก็บได้แค่ค่าที่ใกล้มากๆ
#include <stdio.h>
int main()
{
printf("%f\n", 0.1 + 0.2);
printf("%.17f\n", 0.1 + 0.2);
return 0;
}
0.300000
0.30000000000000004
%.17f คือสั่งให้พิมพ์ทศนิยม 17 ตำแหน่ง (วิธีนี้จะเรียนละเอียดในบทที่ 2) พอดูละเอียดจะเห็นว่า 0.1 + 0.2 ไม่ได้เป็น 0.3 พอดี คลาดไปนิดเดียว ปกติไม่มีผลกับคำตอบ แต่มีผลเมื่อ
- เปรียบเทียบว่าทศนิยมสองค่าเท่ากันไหม ซึ่งจะเจอในบทที่ 3
- เก็บจำนวนเต็มที่ใหญ่มากใน
doubleซึ่งแม่นได้แค่ราว 15 ถึง 16 หลัก
#include <stdio.h>
int main()
{
long long exact = 100000000000000001;
double rough = exact;
printf("%lld\n", exact);
printf("%f\n", rough);
return 0;
}
100000000000000001
100000000000000000.000000
เลข 1 ตัวท้ายหายไปเฉยๆ จึงมีกฎว่า งานที่เป็นจำนวนเต็มให้ใช้ int หรือ long long อย่าใช้ double แม้ double จะเก็บเลขได้ใหญ่กว่าก็ตาม ใช้ double เฉพาะงานที่คำตอบเป็นทศนิยมจริงๆ
char เก็บตัวอักษร
char (มาจาก character) เก็บตัวอักษรหนึ่งตัว ใช้ 1 byte เขียนตัวอักษรในเครื่องหมายคำพูดเดี่ยว 'A' และพิมพ์ด้วย %c
ข้างในคอมพิวเตอร์ไม่มีตัวอักษร มีแต่ตัวเลข ตัวอักษรแต่ละตัวจึงมีรหัสตัวเลขประจำตัวตามตาราง ASCII พิมพ์ char ด้วย %d จะเห็นรหัสนั้น
#include <stdio.h>
int main()
{
char grade = 'B';
printf("%c\n", grade);
printf("%d\n", grade);
printf("%c\n", grade + 1);
printf("%d\n", '7' - '0');
return 0;
}
B
66
C
7
'B'มีรหัส 66 บวก 1 ได้ 67 ซึ่งคือ'C'ตัวอักษรเรียงกันมีรหัสเรียงกัน'7' - '0'ได้ 7 เพราะตัวเลข'0'ถึง'9'มีรหัสเรียงกันเหมือนกัน วิธีนี้ใช้แปลงตัวอักษรตัวเลขเป็นค่าจริง
| ตัวอักษร | รหัส ASCII |
|---|---|
'0' ถึง '9' | 48 ถึง 57 |
'A' ถึง 'Z' | 65 ถึง 90 |
'a' ถึง 'z' | 97 ถึง 122 |
ไม่ต้องท่องตาราง ในโค้ดเขียน 'A' แทน 65 ได้เสมอ อ่านง่ายกว่าด้วย เรื่องตัวอักษรและข้อความจะลงลึกในบทที่ 6
ลองทำให้พัง
แบบที่ 1 หารเสร็จแล้วค่อยเก็บใน double
หาค่าเฉลี่ยคะแนนสามวิชา 70 85 90 ผลรวมคือ 245 ค่าเฉลี่ยที่ถูกคือ 81.666667
#include <stdio.h>
int main()
{
int math, science, english;
scanf("%d %d %d", &math, &science, &english);
double average = (math + science + english) / 3;
printf("%f\n", average);
return 0;
}
70 85 90
81.000000
ได้ทศนิยมหกตำแหน่งก็จริง แต่เป็นศูนย์หมด เพราะ 245 / 3 เป็น int หาร int ได้ 81 ไปแล้ว ค่อยเก็บลงกล่อง double ทีหลัง เป็นกับดักแบบเดียวกับ int คูณ int ในหน้า 1.3 แก้เป็น / 3.0 หรือ (double)(math + science + english) / 3 จะได้ 81.666667
แบบที่ 2 พิมพ์ double ด้วย %d
double height = 1.75;
printf("%d\n", height);
height.c:6:14: warning: format '%d' expects argument of type 'int', but argument 2 has type 'double' [-Wformat=]
6 | printf("%d\n", height);
| ~^ ~~~~~~
| | |
| int double
| %f
printf ไม่แปลง 1.75 เป็น 1 ให้ แต่หยิบข้อมูลผิดที่ไปเลย บนเครื่องที่เขียนเอกสารนี้พิมพ์ออกมาเป็น 0 compiler แนะนำ %f มาให้แล้ว ทำตามนั้น
แบบที่ 3 ใช้เครื่องหมายคำพูดคู่กับ char
char grade = "A";
grade.c:5:18: error: initialization of 'char' from 'char *' makes integer from pointer without a cast [-Wint-conversion]
5 | char grade = "A";
| ^~~
'A' คือตัวอักษรหนึ่งตัว ส่วน "A" คือข้อความ (string) ที่บังเอิญยาวหนึ่งตัว สองอย่างนี้เป็นคนละชนิดกัน ข้อความเก็บใน char ไม่ได้ ข้อความ error ยาวและมีคำว่า pointer ที่ยังไม่ได้เรียน ให้จำแค่ว่า char ใช้เครื่องหมายคำพูดเดี่ยว ข้อความใช้เครื่องหมายคำพูดคู่
แบบที่ 4 ใส่ทศนิยมลง int
#include <stdio.h>
int main()
{
int price = 3.99;
printf("%d\n", price);
return 0;
}
3
ไม่มีคำเตือนจาก -Wall ทศนิยมถูกตัดทิ้งเงียบๆ ราคาหายไป 99 สตางค์ ถ้าค่าใดมีทศนิยมได้ ต้องประกาศเป็น double
สรุป
doubleเก็บทศนิยม รับด้วย%lfพิมพ์ด้วย%fint / intได้intมีข้างใดเป็นdoubleถึงได้ทศนิยม- cast เช่น
(double)xต้องทำก่อนหาร ไม่ใช่หลังหาร - แปลงทศนิยมเป็นจำนวนเต็มคือตัดทิ้งไปทาง 0 ไม่ใช่ปัดเศษ
- ทศนิยมคลาดได้เล็กน้อย งานจำนวนเต็มใช้
intหรือlong longเสมอ charเก็บตัวอักษรหนึ่งตัวในเครื่องหมาย' 'ซึ่งก็คือตัวเลขรหัส ASCII
กลับไปเช็กรายการ ผ่านบทนี้เมื่อ แล้วไปทำแบบฝึกหัด