JavaScript
Number Methods
Number.isSafeInteger
ตรวจสอบว่าตัวเลขอยู่ในช่วง safe integer และเข้าใจว่าเพราะอะไรตัวเลขที่เกินช่วงนี้ถึงสูญเสียความแม่นยำ
Safe Integer คืออะไร
`Number.isSafeInteger(value)` คือ static method ของ `Number` ที่ตรวจสอบว่า `value` อยู่ในช่วง **safe integer** หรือไม่ — safe integer คือเลขจำนวนเต็มที่ JavaScript เก็บด้วยความแม่นยำ 100% โดยไม่มีโอกาสเพี้ยน JavaScript เก็บตัวเลขทุกตัวในรูปแบบ IEEE 754 double-precision floating point (64-bit) — ซึ่งมีพื้นที่เก็บค่าจำนวนเต็มแบบแม่นตรง (mantissa) เพียง 53 bit ทำให้เลขจำนวนเต็มที่มากกว่า `2⁵³ - 1` (หรือ `9007199254740991`) เริ่มสูญเสียความแม่นยำ `Number.isSafeInteger()` จึงเข้มงวดกว่า `Number.isInteger()` — ต่อให้เป็น integer แต่ถ้าเกิน safe range ก็ได้ `false` เรียกใช้แบบ static เสมอ: `Number.isSafeInteger(value)` — รูปแบบเดียวกับ `Number.isInteger()`, `Number.isNaN()`, `Number.isFinite()` ที่เรียนมาแล้ว คืนค่าเป็น `boolean`: `true` เมื่อ `value` เป็น integer และอยู่ในช่วง `Number.MIN_SAFE_INTEGER` ถึง `Number.MAX_SAFE_INTEGER`, `false` ในกรณีอื่นทั้งหมด
// ✅ true — เลขจำนวนเต็มที่อยู่ใน safe range
console.log(Number.isSafeInteger(42)); // true
console.log(Number.isSafeInteger(0)); // true
console.log(Number.isSafeInteger(-7)); // true
// 🔢 ขอบเขต safe integer
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991 (2⁵³ - 1)
console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991 (-(2⁵³ - 1))
// ❌ false — เกิน safe range
console.log(Number.isSafeInteger(9007199254740992)); // false — เกิน MAX_SAFE_INTEGER
// ❌ false — มีทศนิยม
console.log(Number.isSafeInteger(3.14)); // false — มีทศนิยม
// ❌ false — NaN, Infinity
console.log(Number.isSafeInteger(NaN)); // false
console.log(Number.isSafeInteger(Infinity)); // false
// ❌ false — ไม่ทำ type coercion
console.log(Number.isSafeInteger("42")); // false — string ไม่ใช่ number// 9007199254740992 = MAX_SAFE_INTEGER + 1 — เกิน safe range
console.log(Number.isInteger(9007199254740992)); // true — เป็น integer
console.log(Number.isSafeInteger(9007199254740992)); // false — เกิน safe range!
// ทำไม? เพราะเลขที่เกิน 2⁵³ - 1 เริ่มเพี้ยน
console.log(9007199254740992 === 9007199254740993); // true! — ทั้งคู่ถูกเก็บเป็นค่าเดียวกัน
// 9007199254740991 (MAX_SAFE_INTEGER) — ยัง safe อยู่
console.log(Number.isInteger(9007199254740991)); // true
console.log(Number.isSafeInteger(9007199254740991)); // true — อยู่ใน safe range- `Number.isSafeInteger()` ตรวจสอบว่าค่าเป็น integer ที่อยู่ใน safe range — `MIN_SAFE_INTEGER` ถึง `MAX_SAFE_INTEGER`
- เข้มงวดกว่า `Number.isInteger()` — `Number.isInteger()` ดูแค่ว่าเป็น integer หรือไม่ แต่ `Number.isSafeInteger()` ดูด้วยว่าแม่นยำจริงหรือเปล่า
- เรียกบน `Number` constructor โดยตรง — เป็น static method ไม่ต้องมี instance
- ไม่ทำ type coercion โดยเด็ดขาด — string, boolean, null, undefined → `false` ทั้งหมด
- เลขเกิน `2⁵³ - 1` (`9007199254740991`) เริ่มมีโอกาสเพี้ยน — JavaScript เก็บค่าจริงของเลขนั้นไม่ได้
กฎการตรวจสอบ — ทำไมเลขเกิน Safe Integer ถึงเพี้ยน
JavaScript ใช้ IEEE 754 double-precision floating point (64-bit) ในการเก็บตัวเลข — ใน 64 bit นั้น มี 53 bit ใช้เก็บ mantissa (ค่าหลัก) ซึ่งหมายความว่าเลขจำนวนเต็มที่ต้องใช้มากกว่า 53 bit ในการแทนค่าจะเริ่มมี 'ช่องว่าง' (gap) ระหว่างค่า เมื่อเลขเกิน `2⁵³ - 1` (`9007199254740991`): - JavaScript ไม่สามารถแทนค่าทุกจำนวนเต็มได้แบบ 1:1 - เลข 2 ตัวที่ต่างกันอาจถูกเก็บเป็นค่าเดียวกัน (rounding) - ตัวเลขเริ่มเพี้ยน — `9007199254740992 === 9007199254740993` → `true` นี่คือเหตุผลที่ `Number.isSafeInteger()` มีอยู่ — เพื่อบอกว่าเลขตัวนี้ยังอยู่ในช่วงที่ปลอดภัย ไม่มีการเพี้ยน กฎของ `Number.isSafeInteger()`: - คืน `true` เฉพาะกับ integer ในช่วง `[-(2⁵³-1), 2⁵³-1]` - คืน `false` กับ integer ที่เกินช่วงนี้ แม้จะเป็น integer ก็ตาม - คืน `false` กับทศนิยม, `NaN`, `Infinity`, non-number - `Number.isSafeInteger(3.0)` → `true` — เพราะ `3.0 === 3` ใน JavaScript (ไม่มีทศนิยมจริง)
// MAX_SAFE_INTEGER + 1 — เกิน safe range แล้ว
console.log(9007199254740992); // 9007199254740992
// MAX_SAFE_INTEGER + 2 — ควรเป็น 9007199254740993
console.log(9007199254740993); // 9007199254740992 ← ไม่ใช่! ถูกปัดลงเป็นค่าเดียวกับ +1
// สองตัวนี้ === true เพราะ JavaScript เก็บค่าเดียวกัน
console.log(9007199254740992 === 9007199254740993); // true ← เพี้ยนแล้ว!
// MAX_SAFE_INTEGER + 3 — ควรเป็น 9007199254740994
console.log(9007199254740994); // 9007199254740994 ← ยังพอดี (gap = 2)
// MAX_SAFE_INTEGER + 5 — ควรเป็น 9007199254740996
console.log(9007199254740996); // 9007199254740996 ← ยังพอดี
// MAX_SAFE_INTEGER + 6 — ควรเป็น 9007199254740997
console.log(9007199254740997); // 9007199254740996 ← เพี้ยนอีกแล้ว
// Number.isInteger มองว่าเป็น integer (จริง — ไม่มีทศนิยม)
console.log(Number.isInteger(9007199254740992)); // true
// แต่ Number.isSafeInteger มองว่าไม่ safe
console.log(Number.isSafeInteger(9007199254740992)); // false| input | Number.isInteger(input) | Number.isSafeInteger(input) | เหตุผล |
|---|---|---|---|
| 42 | true | true | จำนวนเต็มในช่วง safe range |
| 0 | true | true | 0 เป็น safe integer |
| -7 | true | true | จำนวนเต็มลบในช่วง safe range |
| 3.0 | true | true | 3.0 === 3 — ไม่มีทศนิยมจริง |
| 9007199254740991 | true | true | MAX_SAFE_INTEGER — ยัง safe อยู่ |
| 9007199254740992 | true | false | เกิน MAX_SAFE_INTEGER — ความแม่นยำเริ่มหาย |
| -9007199254740992 | true | false | ต่ำกว่า MIN_SAFE_INTEGER — ไม่ safe |
| 3.14 | false | false | มีทศนิยม — ไม่ใช่ integer |
| NaN | false | false | NaN ไม่ใช่ integer |
| Infinity | false | false | Infinity ไม่ใช่ integer และไม่อยู่ในช่วง safe |
| "42" | false | false | string — ไม่ทำ coercion |
| true | false | false | boolean — ไม่ทำ coercion |
- `Number.isSafeInteger()` จะคืน `false` กับเลขที่เกิน `MAX_SAFE_INTEGER` — แม้จะไม่มีทศนิยมก็ตาม
- เลขที่เกิน `2⁵³ - 1` จะเริ่มมี gap — `x === x + 1` อาจเป็น `true` ได้
- ถ้า API ส่งเลขขนาดใหญ่มา (เช่น Twitter ID, Snowflake ID) — ใช้ `string` หรือ `BigInt` แทน `number`
- `Number.MAX_SAFE_INTEGER` และ `Number.MIN_SAFE_INTEGER` คือขอบเขต safe integer ที่เชื่อถือได้
- ใช้ `Number.isSafeInteger(n) && Number.isInteger(n)` ซ้ำซ้อน — `.isSafeInteger()` ครอบคลุม `.isInteger()` อยู่แล้ว
ใช้เมื่อไหร่
`Number.isSafeInteger()` ใช้เมื่อต้องการความมั่นใจว่าตัวเลขจะไม่เพี้ยนจากการคำนวณ — โดยเฉพาะในสถานการณ์ที่เลขมีค่ามาก เช่น ID ขนาดใหญ่จาก API, การคำนวณทางการเงิน, หรือการทำงานกับข้อมูลที่ต้องการความแม่นยำ 100% สถานการณ์ที่พบบ่อย: - ตรวจสอบ ID จาก API — บางระบบใช้ 64-bit integer เป็น ID (เช่น Twitter, Discord, MongoDB ObjectId แบบ numeric) - ตรวจสอบผลลัพธ์จากการคำนวณ — เลขที่เกิดจาก `Math.pow()` หรือการคูณซ้ำ ๆ อาจหลุด safe range - ใช้ร่วมกับ `parseInt()` หรือ `Number()` — เช็คว่าผลลัพธ์จากการแปลง string เป็น safe integer หรือไม่ - Validate ข้อมูลก่อนเก็บหรือส่งต่อ — ป้องกัน bug ที่เกิดจากเลขเพี้ยน ในทางปฏิบัติมักใช้ validate input ก่อนนำไปใช้ต่อ:
// API ส่งเลข ID ขนาดใหญ่มา — ต้องเช็คว่ายัง safe อยู่
function isValidId(id) {
return Number.isSafeInteger(id) && id > 0;
}
// ID เล็ก — ปกติ
console.log(isValidId(12345)); // true ✅
// ID ใหญ่แต่ยังอยู่ใน safe range
console.log(isValidId(9007199254740991)); // true ✅
// ID เกิน safe range — ไม่น่าใช้เป็น number แล้ว
console.log(isValidId(9007199254740992)); // false — เสี่ยงเพี้ยน
// ค่าที่ไม่ใช่ integer
console.log(isValidId(12.5)); // false
console.log(isValidId(-1)); // false// แปลง string เป็น number แล้วเช็คว่าเป็น safe integer
function parseSafeNumber(raw) {
const num = Number(raw);
if (!Number.isSafeInteger(num)) {
return null; // เกิน safe range — ไม่น่าใช้เป็น number
}
return num;
}
console.log(parseSafeNumber("42")); // 42 ✅
console.log(parseSafeNumber("9007199254740992")); // null — เกิน safe!
console.log(parseSafeNumber("3.14")); // null — ไม่ใช่ integer
console.log(parseSafeNumber("abc")); // null — ไม่ใช่ตัวเลข
console.log(parseSafeNumber("-5")); // -5 ✅
// ตรวจสอบผลลัพธ์จากการคำนวณว่าเพี้ยนหรือไม่
function safeMultiply(a, b) {
const result = a * b;
if (!Number.isSafeInteger(result)) {
return "ผลลัพธ์เกิน safe integer range — อาจเพี้ยน";
}
return result;
}
console.log(safeMultiply(1000, 1000)); // 1000000 ✅
console.log(safeMultiply(100000000, 100000000)); // "ผลลัพธ์เกิน safe integer range..."- ใช้ `Number.isSafeInteger()` เมื่อเลขอาจมีค่ามาก — เช่น ID จาก API, timestamp, ผลคำนวณ
- ใช้ร่วมกับ `Number()` เพื่อแปลงและ validate ในขั้นตอนเดียว — `Number.isSafeInteger(Number(raw))`
- สำหรับเลขขนาดใหญ่มากที่เกิน safe range — พิจารณาใช้ `BigInt` หรือรับค่าเป็น `string`
- ไม่ต้องใช้ `Number.isInteger()` ซ้อนอีก — `Number.isSafeInteger()` เช็คความเป็น integer อยู่แล้ว
- `Number.isSafeInteger(result)` ใช้ตรวจสอบผลลัพธ์ก่อนส่งต่อ ป้องกัน bug ที่หาสาเหตุยาก