JavaScript(JS)在處理大多數數學運算時表現出色。但由於它採用了 IEEE 754 雙精度浮點數標準,導致在處理超高精度小數時,常會遇到精度丟失的問題。例如,直接用JS 算 0.1 + 0.2 會得到令人抓狂的結果:0.30000000000000004以及無法算到小術後第17位以上的運算。這樣的誤差在金融計算或科學計算等需要高精度的會造成嚴重影響。
為了解決這問題,除了使用專門的套件,如 BigNumber.js、decimal.js 等,還有一種手動分割小數來進行四則運算的方法。此方法的是將小數拆解為整數進行運算,然後再手動處理結果的小數部分。以下是用原生JS寫的加減乘除,專門用來處理以上獎的情況。
//加法
function add(a, b) {
let [intA, decA] = a.split('.');
let [intB, decB] = b.split('.');
decA = decA || '';
decB = decB || '';
const maxDecLength = Math.max(decA.length, decB.length);
decA = decA.padEnd(maxDecLength, '0');
decB = decB.padEnd(maxDecLength, '0');
let intSum = BigInt(intA) + BigInt(intB);
let decSum = BigInt(decA) + BigInt(decB);
if (decSum.toString().length > maxDecLength) {
intSum += BigInt(1);
decSum = decSum.toString().slice(1);
}
return intSum.toString() + '.' + decSum.toString().padStart(maxDecLength, '0');
}
//減法
function subtract(a, b) {
let [intA, decA] = a.split('.');
let [intB, decB] = b.split('.');
decA = decA || '';
decB = decB || '';
const maxDecLength = Math.max(decA.length, decB.length);
decA = decA.padEnd(maxDecLength, '0');
decB = decB.padEnd(maxDecLength, '0');
let intDiff = BigInt(intA) - BigInt(intB);
let decDiff = BigInt(decA) - BigInt(decB);
if (decDiff < 0) {
intDiff -= BigInt(1);
decDiff = BigInt('1' + '0'.repeat(maxDecLength)) + decDiff;
}
return intDiff.toString() + '.' + decDiff.toString().padStart(maxDecLength, '0');
}
//乘法
function multiply(a, b) {
let [intA, decA] = a.split('.');
let [intB, decB] = b.split('.');
decA = decA || '';
decB = decB || '';
const totalDecLength = decA.length + decB.length;
const numA = BigInt(intA + decA);
const numB = BigInt(intB + decB);
let product = numA * numB;
let productStr = product.toString();
if (totalDecLength > 0) {
productStr = productStr.padStart(totalDecLength + 1, '0');
const intPart = productStr.slice(0, -totalDecLength);
const decPart = productStr.slice(-totalDecLength);
return intPart + '.' + decPart;
} else {
return productStr;
}
}
//除法(需要手動調整要算到第幾位precision)-預設算到25位
function divide(a, b, precision = 25) {
a = typeof a === 'number' ? a.toString() : a;
b = typeof b === 'number' ? b.toString() : b;
let [intA, decA = ''] = a.split('.');
let [intB, decB = ''] = b.split('.');
const totalDecLengthA = decA.length;
const totalDecLengthB = decB.length;
const numA = BigInt(intA + decA.padEnd(totalDecLengthA, '0'));
const numB = BigInt(intB + decB.padEnd(totalDecLengthB, '0'));
const scaleFactor = BigInt(10 ** (precision + totalDecLengthB - totalDecLengthA));
let quotient = (numA * scaleFactor) / numB;
let quotientStr = quotient.toString().padStart(precision + 1, '0');
const intPart = quotientStr.slice(0, -precision);
const decPart = quotientStr.slice(-precision);
// 如果小數部分全為 0 或者小數點後面不需要保留,則去除小數點部分
if (decPart.replace(/0+$/, '').length === 0) {
return intPart; // 整數結果
} else {
// 保留精度範圍內的小數位數,去除多餘的 0
let result = intPart + '.' + decPart;
result = result.replace(/\.?0+$/, ''); // 移除結尾的多餘零和小數點
return result;
}
}
let a = "1000.99999999999999999999";
let b = "0.00000000000000000001";
let c = "4";
let d = "2";
let resultAdd = add(a, b);
let resultSubtract = subtract(a, b);
let resultMultiply = multiply(a, b);
let resultDivide = divide(a, b, 25);
let resultDivide2 = divide(c, d, 1);
console.log("加法結果:", resultAdd);
console.log("減法結果:", resultSubtract);
console.log("乘法結果:", resultMultiply);
console.log("除法結果:", resultDivide);
console.log("除法結果2:", resultDivide2);
輸出結果

+ There are no comments
Add yours