ข้าม​ไป​ยัง​เนื้อหา

ความ​กว้าง​หมด​ลง และ​ภาษา​ที่​ไม่มี​ความ​กว้าง​ให้​หมด

ออ​เด​อร์​ใบ​เดิม​จาก​บท​ที่ 1 ยัง​อยู่ ยอด​รวม​เป็น 10 ส่วน​คืน​เงิน​ที่​ตาม​มา​ทีหลัง​เป็น 25 และ service ยัง​คำนวณ​ยอด​ที่​ยัง​ต้อง​เก็บ​ด้วย total - refunded เหมือน​เดิม​ทุก​ตัว​อักษร

สิ่ง​ที่​เปลี่ยน​คือ​ภาษา บท​ที่ 1 วัด Rust ภาษา​เดียว​แล้ว​พบ​ว่า​มัน​ตอบ​สอง​อย่าง​จาก source ชุด​เดียว บท​นี้​เอาการ​ลบ​อัน​เดิม​ไป​วาง​ใน​หก​ภาษา แล้ว​ดู​ว่า​ใคร​ตอบ​อะไร

คำถาม​ที่​คน​ถาม​ก่อน​คือ​ภาษา​ไหน​ตอบ​เท่าไร แต่​คำถาม​ที่​แยก​ภาษา​ออก​จาก​กัน​จริงๆ คือ ภาษา​นั้น​มี​ความ​กว้าง​ของ​จำนวนเต็ม​ให้​ประกาศ​หรือ​เปล่า สอง​ภาษา​ใน​หก​ภาษา​นี้​ไม่มี คือ Python ที่ int ขยาย​ตัว​ได้​ไม่​จำกัด และ Node ที่​เก็บ​ค่า​ไว้​ใน double · double เอง​มี​ความ​กว้าง​ของ​มัน​อยู่ สิ่ง​ที่ Node ไม่มี​คือ​ชนิด​จำนวนเต็ม​ความ​กว้าง​ตายตัว​ให้​คุณ​ประกาศ

📦 orderctl — ของ​ชิ้น​เดียวกัน​กับ​คอร์ส​ภาษา​อื่น​ใน​เสา​นี้

orderctl คือ​เครื่อง​มือ​ประมวล​ผล​ออ​เด​อร์​ที่​คอร์ส​ภาษา​ใน​เสา​นี้​สร้าง​ซ้ำ​ที​ละ​ภาษา repo ตัวอย่าง กำลัง​จัด​ทำ ระหว่าง​นี้​ทุก​ตัวเลข​ใน​บท​นี้​รัน​เอง​ได้​ด้วย npm run verify:integer-semantics ซึ่ง​เรียก​ทั้ง​หก​ภาษา​ใน​เครื่อง​เดียว · ไม่​ต้อง​มี Docker และ​ไม่​ต้อง​ต่อ​เน็ต

เจ็ด​แถว​นี้​ให้​คำ​ตอบ​สาม​แบบ และ​ความ​ต่าง​ไม่​ได้​อยู่​ที่​ภาษา แต่​อยู่​ที่​ชนิด​ปลาย​ทาง

ความ​กว้างinteger widthจำนวน bit ที่​ชนิด​หนึ่ง​ใช้​เก็บ​ค่า ซึ่ง​เป็น​ตัว​กำหนด​ช่วง​ของ​มัน · คำถาม​ที่​แยก​ภาษา​ออก​จาก​กัน​คือ​ภาษา​นั้น​มี​ความ​กว้าง​หรือ​ไม่ ไม่ใช่​ว่า​มัน​ตอบ​เท่าไร​ตอน​ความ​กว้าง​หมด คือ​จำนวน bit ที่​ชนิด​หนึ่ง​ใช้​เก็บ​ค่า และ​มัน​เป็น​ตัว​กำหนด​ช่วง​ของ​ชนิด​นั้น ทุก​แถว​ข้าง​ล่าง​คำนวณ 10 - 25 เหมือน​กัน​หมด ต่าง​กัน​ที่​ชนิด​ที่​รับ​ผลลัพธ์

ภาษาชนิดผล​ที่​วัด​ได้หมายเหตุ
Rustu324294967281 หรือ panicตัดสิน​ด้วย -C overflow-checks · บท​ที่ 1
Gouint324294967281เงียบ และ​ไม่มี​ธง​ให้​เปลี่ยน
KotlinUInt4294967281เงียบ
Javaint-15Java ไม่มี​ชนิด unsigned ที่ 32 bit
JavaInteger.toUnsignedString(10 - 25)4294967281bit ชุด​เดิม อ่าน​คนละ​แบบ
Node10 - 25-15 · ส่วน (10-25) >>> 0 ได้ 4294967281>>> เป็น​ตัว​บังคับการ​อ่าน
Python10 - 25-15int ไม่มี​ความ​กว้าง​เลย · 2**64 ได้ 18446744073709551616

ตาราง​นี้​ให้​คำ​ตอบ​สาม​แบบ ไม่ใช่​หก​แบบ​ตาม​จำนวน​ภาษา

  • 4294967281 — Rust u32 · Go uint32 · Kotlin UInt · Java ผ่าน toUnsignedString · และ Node ผ่าน >>> 0
  • -15 — Java int · Node ที่​เขียน 10 - 25 ตรงๆ · และ Python int
  • การ​หยุด​ทำงาน — Rust u32 เมื่อ​เปิด -C overflow-checks

แถว​ที่ 4 กับ​แถว​ที่ 5 แสดง​ว่า -15 กับ 4294967281 มา​จาก bit ชุด​เดียวกัน​ได้

int ของ Python ขยาย​ตัว​ไป​เรื่อยๆ ตาม​ค่าที่​ต้อง​เก็บ มัน​จึง​ไม่มี​ขอบ​ให้​ชน 10 - 25 ของ​มัน ตอบ -15 ตาม​เลขคณิต​ปกติ อย่าง​ที่​แถว​สุดท้าย​ของ​ตาราง​เขียน​ไว้ และ 2**64 ตอบ 18446744073709551616 ออก​มา​ตรงๆ คำถาม​ที่​ไม่มี​ความ​หมาย​กับ​มัน​คือ​คำถาม​เรื่อง​การ​ล้น ไม่ใช่​ทุก​คำถาม​ใน​บท​นี้

ราคา​ที่​จ่าย​ไป​แลก​กับ​เรื่อง​นี้​คือ​ค่า​หนึ่ง​ค่า​ไม่มี​ขนาด​ที่​ตายตัว ตอน​เขียน​ลง database หรือ​ส่ง​ออก​ไป​หา​ภาษา​อื่น ความ​กว้าง​จะ​กลับ​มา​อยู่ดี และ​จุด​นั้น​คือ​ที่​ที่​ค่า​จะ​หาย

JavaScript ไม่มี​ชนิด​จำนวนเต็ม 32 bit ให้​ประกาศ 10 - 25 จึง​เป็นการ​ลบ​ของ​ทศนิยม และ​ผล​ที่​ได้​คือ -15 ตาม​เลขคณิต​ปกติ

แต่ operator ของ​การ​เลื่อน bit บังคับการ​อ่าน​แบบ 32 bit ได้ (10-25) >>> 0 จึง​ได้ 4294967281 ทั้ง​ที่ 10 - 25 ได้ -15 นี่​คือ​ค่า​เดียวกัน อ่าน​ด้วย​กฎ​คนละ​ข้อ

Java ประกาศ unsigned integerunsigned integerชนิด​จำนวนเต็ม​ที่​ไม่มี​ฝั่ง​ลบ · Java ไม่มี​ชนิด​นี้​ที่ 32 bit จึง​ต้อง​อ่าน bit ชุด​เดิม​ผ่าน Integer.toUnsignedString ส่วน Node ต้อง​บังคับการ​อ่าน​ด้วย >>> ที่ 32 bit ไม่​ได้ int เป็น​ชนิด​มี​เครื่องหมาย เสมอ 10 - 25 จึง​ได้ -15 และ​นั่น​คือ​คำ​ตอบ​ที่​ถูก​ต้อง​ของ​ชนิด​นั้น

Integer.toUnsignedString(10 - 25) ตอบ 4294967281 โดย​ไม่​ได้​คำนวณ​ใหม่​สัก​ครั้ง มัน​อ่าน bit ชุด​เดิม​ของ -15 ด้วย​กฎ​แบบ​ไม่มี​เครื่องหมาย แล้ว​เขียน​ออก​มา​เป็น string

แถว​ที่ 4 กับ​แถว​ที่ 5 คือ​แถว​เดียวกัน

-15 กับ 4294967281 ไม่ใช่​คำ​ตอบ​ที่​ขัด​กัน มัน​คือ bit 32 ตัว​ชุด​เดียวกัน​ที่​ถูก​อ่าน​สอง​แบบ Java เลือก​อ่าน​แบบ​มี​เครื่องหมาย​เป็น​ค่า​ตั้ง​ต้น และ​ให้​ทาง​อ่าน​อีก​แบบ​ไว้​ใน​รูป​ของ method แยก

สิ่ง​ที่​ตาม​มา​คือ Java จะ​ไม่มี​ทาง​บอก​คุณ​ว่า​เกิด​อะไร​ขึ้น เพราะ​ใน​สายตา​ของ​มัน 10 - 25 ไม่​ได้ ออก​นอก​ช่วง​ของ int เลย​สัก​นิด ความ​เสีย​หาย​เกิด​ตอน​อีก​ฝั่ง​ของ​สาย​อ่าน​ค่า​เดียวกัน​นี้​แบบ unsigned

flowchart TD
  A["total - refunded · 10 ลบ 25"] --> B{"ชนิดปลายทางมีความกว้างไหม"}
  B -->|"ไม่มี · Python int"| C["-15 · ขยายตัวได้ไม่จำกัด"]
  B -->|"เก็บใน double · Node"| D["-15 · และการเลื่อน bit แบบ unsigned อ่านได้ 4294967281"]
  B -->|"มี 32 bit"| E{"ชนิดนั้นมีฝั่ง unsigned ไหม"}
  E -->|"ไม่มี · Java int"| F["-15 · toUnsignedString อ่าน bit ชุดเดิมได้ 4294967281"]
  E -->|"มี"| G{"มีธงที่เปลี่ยนพฤติกรรมตอนล้นไหม"}
  G -->|"ไม่มี · Go uint32 และ Kotlin UInt"| H["4294967281 · เงียบ"]
  G -->|"มี · Rust u32 กับ -C overflow-checks"| I["4294967281 หรือ panic"]

คำ​บรรยาย​ภาพ: ช่อง​ตัดสิน​สาม​ช่อง​คือ​ความ​กว้าง ฝั่ง unsigned และ​ธง​ที่​เปลี่ยน​พฤติกรรม​ตอน​ล้น ไม่มี​ช่อง​ไหน​ถาม​ชื่อ​ภาษา​เลย Go กับ Kotlin จบ​ที่​ช่อง​เดียวกัน​เพราะ​ตอบ​เหมือน​กัน​ทั้ง​สาม​ช่อง ส่วน Rust แยก​ออก​ไป​ที่​ช่อง​ล่าง​สุด ทั้ง​ที่​ชนิด​ปลาย​ทาง​ของ​มัน​กว้าง​เท่า​กัน​และ unsigned เหมือน​กัน

Kotlin ขึ้น​ตาราง​ที่​บท​นี้​บท​เดียว​ทั้ง​คอร์ส

probe วัด Kotlin ไว้​ทรง​เดียว​คือ UInt ที่​แถว​ที่ 3 ของ​ตาราง​ข้าง​บน ช่อง Kotlin ช่อง​อื่น​จึง​ไม่มี อะไร​ผูก และ​คอร์ส​นี้​จะ​ไม่​พิมพ์​มัน​ออก​มา

ช่อง​ที่ probe ไม่​ได้​วัด คือ​ช่อง​ที่​ไม่มี​หลัก​ฐาน ต่อ​ให้​เดา​ถูก​ก็ตาม

Rust ให้​ยาม​สาม​แบบ Java ให้​ตระกูล Exact ส่วน Go ไม่​ให้​อะไร​เลย

การ​ลบ​แบบ​ธรรมดา​เงียบ​เพราะ​มัน​ไม่มี​ทาง​รายงาน​อะไร​ได้ ผลลัพธ์​ของ​มัน​มี​ช่อง​เดียว​คือ​ค่า การก​ระ​ทำ​แบบ​มี​ยาม​แก้​เรื่อง​นี้​ด้วย​การ​เปลี่ยน​สิ่ง​ที่​คืน​ออก​มา ไม่ใช่​ด้วย​การ​เปลี่ยน​การ​คำนวณ

การ​เรียกค่าที่​วัด​ได้
Rust 10u32.checked_sub(25)None
Rust 10u32.saturating_sub(25)0
Rust 10u32.wrapping_sub(25)4294967281
Java Math.subtractExact(Integer.MIN_VALUE, 1)โยน java.lang.ArithmeticException: integer overflow
Java Math.toIntExact(3000000000L)โยน java.lang.ArithmeticException: integer overflow
Java (int) 3000000000L-1294967296
Go การ​ลบ​ของ uint32ไม่มี​รูป​แบบ​ที่​มี​ยาม​ใน​ภาษา

สาม​แถว​แรก​คือ​การ​ลบ​อัน​เดียวกัน ต่าง​กัน​ที่​สิ่ง​ที่​คืน​กลับ​มา

  • checked_subchecked arithmeticการ​คำนวณ​ที่​คืน​สัญญาณ​เมื่อ​ผลลัพธ์​เกิน​ช่วง แทนที่​จะ​คืน​ค่าที่​ผิด · checked_sub ของ Rust คืน None ส่วน​ตระกูล Exact ของ Java โยน ArithmeticException ออก​มา คืน None ซึ่ง​แปล​ว่า​ผลลัพธ์​ไม่มี​อยู่​ใน​ชนิด​นี้
  • saturating_subsaturating arithmeticการ​คำนวณ​ที่​เมื่อ​ล้น​แล้ว​ค้าง​อยู่​ที่​ขอบ​ของ​ช่วง แทนที่​จะ​วน​ไป​อีก​ฝั่ง · 10u32.saturating_sub(25) จึง​ได้ 0 ไม่ใช่​ค่าที่​วน​กลับ คืน 0 ซึ่ง​เป็น​ขอบ​ล่าง​ของ u32
  • wrapping_subwrapping arithmeticการ​คำนวณ​ที่​เมื่อ​ล้น​แล้ว​วน​กลับ​เข้า​ช่วง​ของ​ชนิด​นั้น แทนที่​จะ​หยุด · เงียบ​ที่สุด​ใน​สาม​แบบ เพราะ​ผลลัพธ์​ที่​ได้​ยัง​เป็น​ค่าที่​ถูก​ต้อง​ตาม​ชนิด​ทุก​ประการ คืน 4294967281 ซึ่ง​เท่ากับ​ที่ rustc -O ตอบ​ใน​บท​ที่ 1

checked_sub เป็น​แบบ​เดียว​ที่​บังคับ​ให้​คุณ​เขียน​ทาง​แยก เพราะ None ไม่ใช่​ตัวเลข อีก​สอง​แบบ คืนตัว​เลข​ที่​ใช้​ต่อ​ได้​ทันที ราคา​ของ​มัน​คือ​คุณ​เลือก​คำ​ตอบ​ที่​ผิด​ไว้​ล่วง​หน้า​แล้ว

ตระกูล Exact ของ Java วัด​ที่​อินพุต​อื่น และ​เหตุผล​ของ​มัน

หัวข้อ​ที่​มีชื่อ​ว่า “ตระกูล Exact ของ Java วัด​ที่​อินพุต​อื่น และ​เหตุผล​ของ​มัน”

10 - 25 ของ Java ได้ -15 ซึ่ง​อยู่​ใน​ช่วง​ของ int สบายๆ มัน​จึง​ไม่ใช่​การ​ล้น และ​ยาม​ก็​ไม่มี อะไร​ให้​จับ probe จึง​วัด Math.subtractExact ที่​คู่​ค่าที่​ออก​นอก​ช่วง​จริง​แทน ส่วน toIntExact รับ​อาร์กิวเมนต์​ตัว​เดียว​เป็น long ไม่ใช่​คู่​ค่า probe จึง​ป้อน 3000000000L ซึ่ง​เกิน​ช่วง​ของ int เข้าไป​ตัว​เดียว

ทั้ง subtractExact และ toIntExact โยน exception ตัว​เดียวกัน​ออก​มา คือ​บรรทัด​นี้

ข้อความ​ของ exception ทั้ง​สอง​การ​เรียก
java.lang.ArithmeticException: integer overflow

แถว​สุดท้าย​ของ Java คือ​แถว​ที่​บอก​ราคา​ของ​การ​ไม่​ใช้​ยาม (int) 3000000000L ได้ -1294967296 ออก​มา​เงียบๆ จาก​ค่า​ตั้ง​ต้น​เดียวกัน​กับ​ที่ toIntExact ปฏิเสธ

Go ไม่มี​การ​เรียก​แบบ checked หรือ saturating ใน​ภาษา​เอง การ​ลบ​ของ uint32 วน​กลับ อย่าง​เดียว และ​ไม่มี​ธง​ระดับ compiler ให้​เปลี่ยน​พฤติกรรม​นั้น​แบบ​ที่ rustc มี

สิ่ง​ที่​เหลือ​ให้​ทำ​คือ​เขียน​การ​ตรวจ​เอง​ก่อน​ลบ ซึ่ง​แปล​ว่า​ความ​ถูก​ต้อง​ของ​เรื่อง​นี้​ย้าย​จาก​ภาษา ไป​อยู่​ที่​วินัย​ของ​คน​เขียน และ​วินัย​มอง​ไม่​เห็น​ตอน​อ่าน diff

สาม​ช่อง​นี้​เท่า​กัน​หมด และ​ตาราง​ที่​ทุก​ช่อง​ต่าง​กัน​ไม่​ได้​จริง​กว่า​ตาราง​ที่​มี​ช่อง​ตรง​กัน

การ​แปลง​ลง​ชนิด​ที่​แคบ​กว่าnarrowing conversionการ​แปลง​ค่า​ไป​ยัง​ชนิด​ที่​แคบ​กว่า ด้วย​การ​ตัด bit ส่วน​บน​ทิ้ง​เงียบๆ · Java Go และ Rust ให้​ผล​ตรง​กัน​ที่ 44 เมื่อ​บีบ 300 ลง​เหลือ 8 bit คือ​การ​บีบ​ค่า​ลง​ไป​ให้​พอดี​กับ​ความ​กว้าง​ที่​น้อย​กว่า เดิม ทั้ง​สาม​ภาษา​ข้าง​ล่าง​บีบ 300 ลง​เหลือ 8 bit แล้ว​ตอบ​ค่า​เดียวกัน

ภาษาสิ่ง​ที่ probe เขียนค่าที่​วัด​ได้
Java(byte) v เมื่อ int v = 30044
Goint8(v) เมื่อ v := 30044
Rustv as u8 เมื่อ v เป็น i32 ค่า 30044

เหตุผล​เดียวกัน​ทั้ง​สาม​ช่อง คือ 8 bit ล่าง​ของ 300 มี​ค่า​เท่ากับ 44 และ​ทั้ง​สาม​ภาษา​เลือก ตัด bit ส่วน​บน​ทิ้ง​โดย​ไม่​รายงาน​อะไร

ทั้ง​สาม​ช่อง​ส่ง​ค่า​ผ่าน​ตัวแปร​ก่อน​เสมอ ไม่มี​ช่อง​ไหน​เขียน 300 ลง​ไป​ใน​การ​แป​ลง​ตรงๆ ด้วย​เหตุผล รูป​เดียว​กับ​กับ​ดัก literal ใน​บท​ที่ 1 คือ​ค่าที่ compiler มอง​เห็น​ล่วง​หน้า​ไม่ใช่​ค่าที่​มา​ถึง​ตอน runtime

Go เป็น​ช่อง​ที่​เห็น​ความ​ต่าง​นี้​ชัด​ที่สุด เพราะ int8(300) ที่​เขียน​ตรงๆ ไม่​ผ่าน compile เลย ตาราง​ที่​เขียน​ด้วย literal จึง​ไม่ใช่​ตาราง​ของ​การ​ตัด​ทอน​ตอน runtime แต่​เป็น​ตาราง​ของ compiler

ช่อง​ที่​ตรง​กัน​คือ​ผล​การ​วัด ไม่ใช่​ช่อง​ที่​เขียน​ไม่​เสร็จ

ตาราง​เปรียบ​เทียบ​มัก​ถูก​เขียน​ให้​ทุก​ช่อง​ต่าง​กัน เพราะ​ตาราง​แบบ​นั้น​ดู​เหมือน​มี​อะไร​ให้​สอน แต่​ความ​ตรง​กัน​ของ​สาม​ช่อง​นี้​เป็น​สิ่ง​ที่​วัด​มา​ได้​จริง ไม่ใช่​ช่อง​ที่​ยัง​ไม่​ได้​กรอก

ประโยชน์​ของ​มัน​ตรง​ไป​ตรง​มา คือ​คุณ​ย้าย​การ​ตัด​ทอน​แบบ​นี้​ข้าม​สาม​ภาษา​ได้​โดย​ไม่​ต้อง​จำ​สาม​กฎ สิ่ง​ที่​ต้อง​จำ​แยก​คือ​เรื่อง​ที่​บท​นี้​วัด​ได้​ว่า​ต่าง​จริง ไม่ใช่​ทุก​เรื่อง​ที่​ชื่อ​ภาษา​ต่าง​กัน

บท​นี้​ปิด​คำถาม​เรื่อง​ความ​กว้าง บท​ถัด​ไป​ถาม​ค่าที่​แม้แต่​ความ​กว้าง​ที่​พอ​ก็​ยัง​ตอบ​ผิด

ธี​สิ​ส​ของ​คอร์ส​คือ​ค่า​จำนวนเต็ม​ค่า​หนึ่ง​เปลี่ยน​ได้​ห้า​จุด​ระหว่าง​ทาง และ​ทุก​จุด​เงียบ บท​ที่ 1 วัด​จุด​แรก​ใน​ภาษา​เดียว บท​นี้​วัด​จุด​เดียวกัน​ข้าม​หก​ภาษา แล้ว​ได้​ข้อ​สรุป​ที่​ไม่ใช่​เรื่อง​ของ​ภาษา

  • ชนิด​ปลาย​ทาง​กับ​ธง​เป็น​คน​ตัดสิน ไม่ใช่​ชื่อ​ภาษา · Go กับ Kotlin ตอบ​ตรง​กัน​เพราะ​ชนิด​ตรง​กัน และ​ไม่มี​ธง​ทั้ง​คู่ ส่วน Rust ถือ​ชนิด​เดียวกัน​แต่​แยก​ออก​ไป​เพราะ​มี​ธง
  • สอง​ภาษา​ไม่มี​ความ​กว้าง​ของ​จำนวนเต็ม​ให้​หมด คือ Python ที่ int ขยาย​ได้ และ Node ที่​เก็บ​ค่า​ไว้​ใน double
  • ยาม​มี​อยู่​แค่​ใน​บาง​ภาษา และ​มี​แบบ​เดียว​ที่​บังคับ​ให้​คุณ​เขียน​ทาง​แยก คือ checked_sub ที่​คืน None ส่วน saturating_sub กับ wrapping_sub คืนตัว​เลข​ที่​ใช้​ต่อ​ได้​ทันที

บท​ที่ 3 ไป​ที่​ค่าที่​อยู่​ใน​ช่วง​เรียบร้อย​แล้ว แต่​ยัง​ตอบ​ผิด​อยู่ดี คือ​ค่าสัมบูรณ์​ของ Integer.MIN_VALUE ที่ Math.abs คืน​ออก​มา​เป็น​ค่า​ติดลบ โดย​ไม่มี exception สัก​ตัว


🔗 อ้างอิง​ต้นทาง​ของ​บท​นี้
  • Java 25 API — java.lang.Math (เข้า​ถึง 2026-09-13) — ที่มา​ของ subtractExact ที่​หน้า​นี้​ระบุ​ว่า​โยน ArithmeticException เมื่อ​ผลลัพธ์​ล้น int และ​ของ toIntExact ที่​โยน exception ตัว​เดียวกัน​เมื่อ​อาร์กิวเมนต์​ล้น int

  • Rust std — primitive type u32 (เข้า​ถึง 2026-09-13) — ที่มา​ของ​ยาม​สาม​แบบ คือ checked_sub ที่​หน้า​นี้​อธิบาย​ว่า​คืน None เมื่อ​เกิด​การ​ล้น และ saturating_sub กับ wrapping_sub ที่​อยู่​ใน​รายการ method ของ​ชนิด​เดียวกัน

  • The Go Programming Language Specification — Numeric types (เข้า​ถึง 2026-09-13) — ที่มา​ของ​การ​ที่ Go ประกาศ​ชนิด​ความ​กว้าง​ตายตัว​อย่าง uint32 กับ int8 ไว้​ใน​ภาษา และ​ระบุ​ว่า​จำนวนเต็ม n bit เก็บ​ด้วย​การ​เข้า​รหัส​แบบ two’s complement

  • ตัวเลข​ทุก​ตัว​ใน​บท​นี้​มา​จาก scripts/integer-semantics/measured.json ซึ่ง npm run verify:integer-semantics สร้าง​ขึ้น​ใหม่​ได้ · วัด​บน rustc 1.97.1 · go1.27.0 · openjdk 25.0.4.1 · kotlinc-jvm 2.4.10 · node v24.19.0 · Python 3.14.6 บน darwin/arm64 เมื่อ 2026-09-12

เช็กความเข้าใจ — บทที่ 2

ข้อ 1 / 3

`10 - 25` ของ Java ได้ `-15` ส่วน `Integer.toUnsignedString(10 - 25)` ได้ `4294967281` สองค่านี้ต่างกันเพราะอะไร