ไทย

คู่มือฉบับสมบูรณ์เกี่ยวกับ BigInt ใน JavaScript ครอบคลุมคุณสมบัติ การใช้งาน และการประยุกต์ใช้ในการจัดการเลขจำนวนเต็มขนาดใหญ่ เรียนรู้วิธีเอาชนะข้อจำกัดของ JavaScript และคำนวณที่ซับซ้อนได้อย่างแม่นยำ

JavaScript BigInt: การจัดการเลขจำนวนเต็มขนาดใหญ่

JavaScript แม้จะเป็นภาษาที่มีความหลากหลาย แต่ก็มีข้อจำกัดในการจัดการกับจำนวนเต็มที่มีขนาดใหญ่มาก ๆ ชนิดข้อมูลมาตรฐาน `Number` สามารถแสดงค่าจำนวนเต็มได้อย่างแม่นยำจนถึงขีดจำกัดที่เรียกว่า `Number.MAX_SAFE_INTEGER` เท่านั้น หากเกินขีดจำกัดนี้ การคำนวณจะเริ่มไม่แม่นยำและนำไปสู่ผลลัพธ์ที่ไม่คาดคิด และนี่คือจุดที่ BigInt เข้ามาช่วย BigInt ซึ่งถูกนำมาใช้ใน ECMAScript 2020 เป็นออบเจ็กต์ในตัวที่ช่วยให้สามารถแสดงและจัดการกับจำนวนเต็มขนาดใดก็ได้ โดยไม่มีข้อจำกัดเหมือนกับชนิดข้อมูล `Number` แบบมาตรฐาน

ทำความเข้าใจถึงความจำเป็นของ BigInt

ก่อนที่จะมี BigInt นักพัฒนา JavaScript ต้องพึ่งพาไลบรารีหรือสร้างวิธีการขึ้นมาเองเพื่อจัดการกับการคำนวณเลขจำนวนเต็มขนาดใหญ่ ซึ่งโซลูชันเหล่านี้มักจะมาพร้อมกับภาระด้านประสิทธิภาพและความซับซ้อนที่เพิ่มขึ้น การมาของ BigInt ได้มอบวิธีการที่เป็นมาตรฐานและมีประสิทธิภาพในการทำงานกับจำนวนเต็มขนาดใหญ่ เปิดโอกาสสำหรับการใช้งานในด้านต่าง ๆ มากมาย รวมถึง:

การสร้างค่า BigInt

มีสองวิธีหลักในการสร้างค่า BigInt ใน JavaScript:

  1. การใช้ constructor `BigInt()`: constructor นี้สามารถแปลงค่าตัวเลข, สตริง หรือบูลีนให้เป็น BigInt ได้
  2. การใช้ส่วนต่อท้าย `n`: การเติม `n` ต่อท้ายตัวเลขจำนวนเต็มจะสร้างค่า BigInt

ตัวอย่าง:

การใช้ constructor `BigInt()`:


const bigIntFromNumber = BigInt(12345678901234567890);
const bigIntFromString = BigInt("98765432109876543210");
const bigIntFromBoolean = BigInt(true); // ผลลัพธ์คือ 1n
const bigIntFromFalseBoolean = BigInt(false); // ผลลัพธ์คือ 0n

console.log(bigIntFromNumber); // ผลลัพธ์: 12345678901234567890n
console.log(bigIntFromString); // ผลลัพธ์: 98765432109876543210n
console.log(bigIntFromBoolean); // ผลลัพธ์: 1n
console.log(bigIntFromFalseBoolean); // ผลลัพธ์: 0n

การใช้ส่วนต่อท้าย `n`:


const bigIntLiteral = 12345678901234567890n;
console.log(bigIntLiteral); // ผลลัพธ์: 12345678901234567890n

หมายเหตุสำคัญ: คุณไม่สามารถนำค่า BigInt และ Number มาใช้ในการดำเนินการทางคณิตศาสตร์ร่วมกันโดยตรงได้ คุณต้องแปลงให้เป็นชนิดเดียวกันก่อนทำการคำนวณ การพยายามนำมาใช้ร่วมกันโดยตรงจะทำให้เกิด `TypeError`

การดำเนินการทางคณิตศาสตร์ของ BigInt

BigInt รองรับตัวดำเนินการทางคณิตศาสตร์มาตรฐานส่วนใหญ่ ซึ่งรวมถึง:

ตัวอย่าง:


const a = 12345678901234567890n;
const b = 98765432109876543210n;

const sum = a + b;
const difference = a - b;
const product = a * b;
const quotient = a / 2n; // หมายเหตุ: การหารจะปัดเศษทิ้งเข้าหาศูนย์
const remainder = a % 7n;
const power = a ** 3n; // การยกกำลังทำงานตามที่คาดหวัง

console.log("Sum:", sum); // ผลลัพธ์: Sum: 111111111011111111100n
console.log("Difference:", difference); // ผลลัพธ์: Difference: -86419753208641975320n
console.log("Product:", product); // ผลลัพธ์: Product: 1219326311370217957951669538098765432100n
console.log("Quotient:", quotient); // ผลลัพธ์: Quotient: 6172839450617283945n
console.log("Remainder:", remainder); // ผลลัพธ์: Remainder: 5n
console.log("Power:", power); // ผลลัพธ์: Power: 187641281029182300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n

ข้อควรพิจารณาที่สำคัญ:

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

คุณสามารถใช้ตัวดำเนินการเปรียบเทียบมาตรฐาน (`==`, `!=`, `<`, `>`, `<=`, `>=`) เพื่อเปรียบเทียบค่า BigInt กับค่า BigInt อื่น ๆ หรือแม้กระทั่งกับค่า Number อย่างไรก็ตาม ควรระวังเกี่ยวกับการแปลงชนิดข้อมูลโดยอัตโนมัติ (type coercion)

ตัวอย่าง:


const a = 10n;
const b = 20n;
const c = 10;

console.log(a == b);   // ผลลัพธ์: false
console.log(a != b);   // ผลลัพธ์: true
console.log(a < b);    // ผลลัพธ์: true
console.log(a > b);    // ผลลัพธ์: false
console.log(a <= b);   // ผลลัพธ์: true
console.log(a >= b);   // ผลลัพธ์: false

console.log(a == c);   // ผลลัพธ์: true (มีการแปลงชนิดข้อมูล)
console.log(a === c);  // ผลลัพธ์: false (ไม่มีการแปลงชนิดข้อมูล)

แนวทางปฏิบัติที่ดีที่สุด: ใช้การเปรียบเทียบแบบเข้มงวด (`===`) และการไม่เท่ากันแบบเข้มงวด (`!==`) เพื่อหลีกเลี่ยงการแปลงชนิดข้อมูลที่ไม่คาดคิดเมื่อเปรียบเทียบค่า BigInt และ Number

การแปลงระหว่าง BigInt และ Number

แม้ว่าการดำเนินการทางคณิตศาสตร์โดยตรงระหว่าง BigInt และ Number จะไม่ได้รับอนุญาต แต่คุณสามารถแปลงระหว่างสองชนิดข้อมูลนี้ได้ อย่างไรก็ตาม โปรดระวังการสูญเสียความแม่นยำที่อาจเกิดขึ้นเมื่อแปลง BigInt เป็น Number หากค่า BigInt นั้นเกิน `Number.MAX_SAFE_INTEGER`

ตัวอย่าง:


const bigIntValue = 9007199254740991n; // Number.MAX_SAFE_INTEGER
const numberValue = Number(bigIntValue); // การแปลง BigInt เป็น Number
console.log(numberValue); // ผลลัพธ์: 9007199254740991

const largerBigIntValue = 9007199254740992n; // เกิน Number.MAX_SAFE_INTEGER
const largerNumberValue = Number(largerBigIntValue);
console.log(largerNumberValue); // ผลลัพธ์: 9007199254740992 (อาจไม่แม่นยำ)

const numberToBigInt = BigInt(12345); // การแปลง Number เป็น BigInt
console.log(numberToBigInt); // ผลลัพธ์: 12345n

กรณีการใช้งานและตัวอย่าง

การเข้ารหัส (Cryptography)

อัลกอริทึมการเข้ารหัสมักใช้จำนวนเฉพาะที่มีขนาดใหญ่มากเพื่อความปลอดภัย BigInt ช่วยให้สามารถแสดงและจัดการกับตัวเลขเหล่านี้ได้อย่างมีประสิทธิภาพ


// ตัวอย่าง: การสร้างคู่คีย์แบบง่าย (ไม่ปลอดภัย)
function generateKeyPair() {
  const p = 281n; // จำนวนเฉพาะ
  const q = 283n; // จำนวนเฉพาะอีกตัว
  const n = p * q; // โมดูลัส
  const totient = (p - 1n) * (q - 1n); // ฟังก์ชัน totient ของออยเลอร์

  // เลือก e (public exponent) โดยที่ 1 < e < totient และ gcd(e, totient) = 1
  const e = 17n;

  // คำนวณ d (private exponent) โดยที่ (d * e) % totient = 1
  let d = 0n;
  for (let i = 1n; i < totient; i++) {
    if ((i * e) % totient === 1n) {
      d = i;
      break;
    }
  }

  return {
    publicKey: { n, e },
    privateKey: { n, d },
  };
}

const keyPair = generateKeyPair();
console.log("Public Key:", keyPair.publicKey);
console.log("Private Key:", keyPair.privateKey);

หมายเหตุ: นี่เป็นตัวอย่างที่เรียบง่ายเพื่อการสาธิตเท่านั้น การเข้ารหัสในโลกแห่งความเป็นจริงใช้จำนวนเฉพาะที่ใหญ่กว่ามากและอัลกอริทึมที่ซับซ้อนกว่านี้

การคำนวณทางการเงิน

เมื่อต้องจัดการกับเงินจำนวนมาก โดยเฉพาะในการทำธุรกรรมระหว่างประเทศ ความแม่นยำเป็นสิ่งสำคัญอย่างยิ่ง BigInt สามารถป้องกันข้อผิดพลาดจากการปัดเศษและรับประกันการคำนวณที่ถูกต้อง


// ตัวอย่าง: การคำนวณดอกเบี้ยทบต้น
function calculateCompoundInterest(principal, rate, time) {
  const principalBigInt = BigInt(principal * 100); // แปลงเป็นหน่วยเซ็นต์
  const rateBigInt = BigInt(rate * 10000);       // แปลงเป็นหนึ่งในหมื่นของเปอร์เซ็นต์
  const timeBigInt = BigInt(time);

  let amount = principalBigInt;
  for (let i = 0n; i < timeBigInt; i++) {
    amount = amount * (10000n + rateBigInt) / 10000n;
  }

  const amountInDollars = Number(amount) / 100;
  return amountInDollars;
}

const principal = 1000000; // $1,000,000
const rate = 0.05;    // อัตราดอกเบี้ย 5%
const time = 10;     // 10 ปี

const finalAmount = calculateCompoundInterest(principal, rate, time);
console.log("Final Amount:", finalAmount); // ผลลัพธ์: Final Amount: 1628894.6267774413 (โดยประมาณ)

ในตัวอย่างนี้ เราแปลงเงินต้นและอัตราดอกเบี้ยเป็นค่า BigInt เพื่อหลีกเลี่ยงข้อผิดพลาดจากการปัดเศษระหว่างการคำนวณ จากนั้นผลลัพธ์จะถูกแปลงกลับเป็น Number เพื่อการแสดงผล

การทำงานกับ ID ขนาดใหญ่

ในระบบแบบกระจาย (distributed systems) การสร้าง ID ที่ไม่ซ้ำกันบนเซิร์ฟเวอร์หลายเครื่องอาจเป็นเรื่องท้าทาย การใช้ BigInt ช่วยให้คุณสามารถสร้าง ID ที่มีขนาดใหญ่มาก ซึ่งมีโอกาสน้อยที่จะชนกัน


// ตัวอย่าง: การสร้าง ID ที่ไม่ซ้ำกันโดยใช้ timestamp และ ID ของเซิร์ฟเวอร์
function generateUniqueId(serverId) {
  const timestamp = BigInt(Date.now());
  const serverIdBigInt = BigInt(serverId);
  const random = BigInt(Math.floor(Math.random() * 1000)); // เพิ่มการสุ่มเล็กน้อย

  // รวมค่าต่าง ๆ เพื่อสร้าง ID ที่ไม่ซ้ำกัน
  const uniqueId = (timestamp << 20n) + (serverIdBigInt << 10n) + random;
  return uniqueId.toString(); // คืนค่าเป็นสตริงเพื่อให้ง่ายต่อการจัดการ
}

const serverId = 123; // ID เซิร์ฟเวอร์ตัวอย่าง
const id1 = generateUniqueId(serverId);
const id2 = generateUniqueId(serverId);

console.log("Unique ID 1:", id1);
console.log("Unique ID 2:", id2);

BigInt และ JSON

ค่า BigInt ไม่ได้รับการสนับสนุนโดย JSON โดยกำเนิด การพยายาม serialize ออบเจ็กต์ JavaScript ที่มี BigInt โดยใช้ `JSON.stringify()` จะทำให้เกิด `TypeError` ในการจัดการค่า BigInt เมื่อทำงานกับ JSON คุณมีสองทางเลือก:

  1. แปลงเป็นสตริง: แปลง BigInt เป็นสตริงก่อนทำการ serialize นี่เป็นวิธีที่พบบ่อยและตรงไปตรงมาที่สุด
  2. การ Serialize/Deserialize แบบกำหนดเอง: ใช้ฟังก์ชัน serialize/deserialize แบบกำหนดเองเพื่อจัดการค่า BigInt

ตัวอย่าง:

การแปลงเป็นสตริง:


const data = {
  id: 12345678901234567890n,
  name: "Example Data",
};

// แปลง BigInt เป็นสตริงก่อนทำการ serialize
data.id = data.id.toString();

const jsonData = JSON.stringify(data);
console.log(jsonData); // ผลลัพธ์: {"id":"12345678901234567890","name":"Example Data"}

// เมื่อทำการ deserialize คุณจะต้องแปลงสตริงกลับเป็น BigInt
const parsedData = JSON.parse(jsonData, (key, value) => {
  if (key === "id") {
    return BigInt(value);
  }
  return value;
});

console.log(parsedData.id); // ผลลัพธ์: 12345678901234567890n

การ Serialize/Deserialize แบบกำหนดเอง (โดยใช้ `replacer` และ `reviver`):


const data = {
  id: 12345678901234567890n,
  name: "Example Data",
};

// การ serialize แบบกำหนดเอง
const jsonData = JSON.stringify(data, (key, value) => {
  if (typeof value === 'bigint') {
    return value.toString();
  } else {
    return value;
  }
});

console.log(jsonData);

// การ deserialize แบบกำหนดเอง
const parsedData = JSON.parse(jsonData, (key, value) => {
    if (typeof value === 'string' && /^[0-9]+$/.test(value)) { // ตรวจสอบว่าเป็นสตริงตัวเลขหรือไม่
      try {
        return BigInt(value);
      } catch(e) {
          return value;
      }
    }
    return value;
});

console.log(parsedData.id);

ความเข้ากันได้ของเบราว์เซอร์

BigInt ได้รับการสนับสนุนอย่างกว้างขวางในเบราว์เซอร์สมัยใหม่ อย่างไรก็ตาม การตรวจสอบความเข้ากันได้สำหรับเบราว์เซอร์หรือสภาพแวดล้อมที่เก่ากว่าเป็นสิ่งสำคัญ คุณสามารถใช้เครื่องมืออย่าง Can I use เพื่อตรวจสอบการสนับสนุนของเบราว์เซอร์ หากคุณต้องการสนับสนุนเบราว์เซอร์รุ่นเก่า คุณอาจพิจารณาใช้ polyfill แต่โปรดทราบว่า polyfill อาจส่งผลต่อประสิทธิภาพ

ข้อควรพิจารณาด้านประสิทธิภาพ

แม้ว่า BigInt จะเป็นวิธีที่มีประสิทธิภาพในการทำงานกับจำนวนเต็มขนาดใหญ่ แต่ก็ควรตระหนักถึงผลกระทบที่อาจเกิดขึ้นกับประสิทธิภาพ

ดังนั้น ควรใช้ BigInt เฉพาะเมื่อจำเป็นเท่านั้น และปรับโค้ดของคุณให้มีประสิทธิภาพสูงสุดหากคุณต้องทำการดำเนินการกับ BigInt จำนวนมาก

สรุป

BigInt เป็นส่วนเสริมที่มีค่าสำหรับ JavaScript ซึ่งช่วยให้นักพัฒนาสามารถจัดการกับการคำนวณเลขจำนวนเต็มขนาดใหญ่ได้อย่างแม่นยำ ด้วยการทำความเข้าใจคุณสมบัติ ข้อจำกัด และกรณีการใช้งาน คุณสามารถใช้ประโยชน์จาก BigInt เพื่อสร้างแอปพลิเคชันที่แข็งแกร่งและแม่นยำในด้านต่าง ๆ รวมถึงการเข้ารหัส การคำนวณทางการเงิน และการคำนวณทางวิทยาศาสตร์ อย่าลืมพิจารณาความเข้ากันได้ของเบราว์เซอร์และผลกระทบด้านประสิทธิภาพเมื่อใช้ BigInt ในโปรเจกต์ของคุณ

แหล่งข้อมูลเพิ่มเติม

คู่มือนี้ให้ภาพรวมที่ครอบคลุมของ BigInt ใน JavaScript สำรวจแหล่งข้อมูลที่เชื่อมโยงไว้สำหรับข้อมูลเชิงลึกและเทคนิคขั้นสูงเพิ่มเติม