[0]zero-index clytie0x/zero-index

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 พิมพ์ด้วย %f
  • int / int ได้ int มีข้างใดเป็น double ถึงได้ทศนิยม
  • cast เช่น (double)x ต้องทำก่อนหาร ไม่ใช่หลังหาร
  • แปลงทศนิยมเป็นจำนวนเต็มคือตัดทิ้งไปทาง 0 ไม่ใช่ปัดเศษ
  • ทศนิยมคลาดได้เล็กน้อย งานจำนวนเต็มใช้ int หรือ long long เสมอ
  • char เก็บตัวอักษรหนึ่งตัวในเครื่องหมาย ' ' ซึ่งก็คือตัวเลขรหัส ASCII

กลับไปเช็กรายการ ผ่านบทนี้เมื่อ แล้วไปทำแบบฝึกหัด