詳解JavaScript中任意兩數(shù)加減的解決方案
寫(xiě)在前面
本文是從初步解決到最終解決的思路,文章篇幅較長(zhǎng)
雖然是一篇從0開(kāi)始的文章,中間的思維跳躍可能比較大
代碼的解析都在文章的思路分析和注釋里,全文會(huì)幫助理解的幾個(gè)關(guān)鍵詞
1.Number.MAX_SAFE_INTEGER 和 Number.MIN_SAFE_INTEGER
2.15長(zhǎng)度的字符串
3.padStart 和 padEnd
分析填坑思路
相信很多人都知道這是怎么回事吧
console.log( 0.1 + 0.2 === 0.3 ) // false console.log( 0.3 - 0.2 === 0.1) // false
不了解的出門右拐自己去查詢下,這里就不一一解釋了!
通過(guò)上面的例子可以知道,小數(shù)點(diǎn)的加減是存在問(wèn)題的,那么有什么解決方式呢?
既然小數(shù)點(diǎn)加減有問(wèn)題,那就先來(lái)整數(shù)進(jìn)行加減吧,這個(gè)應(yīng)該就沒(méi)什么問(wèn)題了小數(shù)點(diǎn)的加減自行百度解決,可以通過(guò)浮點(diǎn)計(jì)算,這里就不介紹了,那么整數(shù)的加減就一定沒(méi)有任何問(wèn)題嗎?來(lái)看看下面的例子
const MAX = Number.MAX_SAFE_INTEGER; console.log( MAX ) // 9007199254740991 console.log( MAX + 2 ) // 9007199254740992
Number.MAX_SAFE_INTEGER是什么?
常量表示在 JavaScript 中最大的安全整數(shù)
所以,Number.MIN_SAFE_INTEGER就是最小安全系數(shù)
顧名思義,就是在JavaScript中加減法在這兩個(gè)范圍內(nèi)是穩(wěn)定的,是不是這樣就安全了?好像還是有點(diǎn)小問(wèn)題:
console.log( 10**21 ) // 1e+21 console.log(9999999999999999) // 9999999999999999 console.log(99999999999999999) // 10000000000000000 console.log(999999999999999999999) // 1e+21
從上面的結(jié)果來(lái)看,是不安全的
1.最后的結(jié)果是科學(xué)計(jì)數(shù)法
2.不知道具體的真實(shí)數(shù)據(jù)是多少
既然數(shù)字的顯示存在這樣的問(wèn)題,把輸入結(jié)果和輸出結(jié)果都用字符串表示
console.log(`${10 ** 21}`)
// '1e+21'
console.log('' + 10 ** 21)
// '1e+21'
console.log((10 ** 21).toString())
// '1e+21'
我們發(fā)現(xiàn)即使直接就轉(zhuǎn)換成字符串仍然會(huì)顯示為科學(xué)計(jì)數(shù)法,那么可以直接輸入字符串了,跳過(guò)轉(zhuǎn)成字符串的過(guò)程
解決整數(shù)加減的坑
先分析下可能性
1.輸入的數(shù)字在安全系數(shù)范圍內(nèi),且計(jì)算結(jié)果也在安全系數(shù)范圍內(nèi),這種直接輸出結(jié)果
2.不符合條件1(TODO)
const MAX = Number.MAX_SAFE_INTEGER;
const MIN = Number.MIN_SAFE_INTEGER;
/**
* @Description: 判斷輸入的數(shù)字是否在javascript的安全系數(shù)范圍內(nèi)
* @param { number } 需要檢查的數(shù)字
* @return { boolean }: 返回?cái)?shù)字是否為安全的整數(shù)
*/
function isSafeNumber(num) {
// 即使 num 成了科學(xué)計(jì)數(shù)法也能正確的和 MAX, MIN 比較大小
return MIN <= num && num <= MAX;
}
/**
* @Description: 計(jì)算兩個(gè)數(shù)之和,返回計(jì)算結(jié)果
* @param { String }: a 相加的第一個(gè)整數(shù)字符串
* @param { String }: b 相加的第一個(gè)整數(shù)字符串
* @return { string }: 返回計(jì)算結(jié)果
*/
function IntAdd(a = "", b = "") {
let result = "0";
const intA = Number(a),
intB = Number(b);
if (intA === 0) return intB;
if (intB === 0) return intA;
if (
isSafeNumber(intA) &&
isSafeNumber(intB) &&
isSafeNumber(intA + intB)
) {
result = intA + intB;
} else {
result = IntCalc(a, b);
}
return result;
}
function IntCalc(a, b) {
// TODO
}
function resClick() {
const a = document.getElementById("ipt1").value;
const b = document.getElementById("ipt2").value;
const result = IntAdd(a, b);
document.getElementById("res").innerText = result;
}
如果不滿足上面條件的呢?
思路:
獲取數(shù)字轉(zhuǎn)成字符串拆分成多個(gè)部分(數(shù)組),每一個(gè)部分的長(zhǎng)度為 Number.MAX_SAFE_INTEGER 轉(zhuǎn)成字符串后的長(zhǎng)度減一(15),長(zhǎng)度不足15的用字符‘0’填充首部,再計(jì)算每個(gè)部分的結(jié)果后拼接在一起
同時(shí)考慮到正負(fù)號(hào)的問(wèn)題,拆分后的計(jì)算需要帶上符號(hào)
長(zhǎng)度減一的原因是接下來(lái)每部分的所有計(jì)算都是安全的,不需要在考慮是數(shù)字計(jì)算結(jié)果為安全的整數(shù)
同時(shí)每部分計(jì)算后的結(jié)果存在問(wèn)題以及解決方案
注意:下面會(huì)使用15這個(gè)數(shù)字,15上面說(shuō)過(guò)了,是Number.MAX_SAFE_INTEGER的長(zhǎng)度減一
1.計(jì)算結(jié)果為0
那么這個(gè)部分賦值15個(gè)字符‘0’組成的字符串,即‘000000000000000’
2.計(jì)算結(jié)果為負(fù)數(shù)
那么向上一級(jí)數(shù)組借10的15次方,同時(shí)高位(下一級(jí)數(shù)組)減一,低位用10的15次方再加上這個(gè)負(fù)數(shù),做為這個(gè)部分的結(jié)果
3.計(jì)算結(jié)果為正數(shù),判斷長(zhǎng)度:
如果長(zhǎng)度超過(guò)15,那么去掉結(jié)果的第一位字符(因?yàn)檫M(jìn)位,第一個(gè)字符一定是‘1’),同時(shí)高位(下一級(jí)數(shù)組)加一
如果長(zhǎng)度沒(méi)有超過(guò)15,向首部補(bǔ)充0直到長(zhǎng)度足夠15
如果長(zhǎng)度等于15,直接添加到結(jié)果中
改造上面的代碼:
const MAX = Number.MAX_SAFE_INTEGER;
const MIN = Number.MIN_SAFE_INTEGER;
const intLen = `${MAX}`.length - 1;
/**
* @Description: 判斷輸入的數(shù)字是否在javascript的安全系數(shù)范圍內(nèi)
* @param { number } 需要檢查的數(shù)字
* @return { boolean }: 返回?cái)?shù)字是否為安全的整數(shù)
*/
function isSafeNumber(num) {
// 即使 num 成了科學(xué)計(jì)數(shù)法也能正確的和 MAX, MIN 比較大小
return MIN <= num && num <= MAX;
}
/**
* @Description: 計(jì)算兩個(gè)數(shù)之和,返回計(jì)算結(jié)果
* @param { String }: a 相加的第一個(gè)整數(shù)字符串
* @param { String }: b 相加的第一個(gè)整數(shù)字符串
* @return { string }: 返回計(jì)算結(jié)果
*/
function IntAdd(a = "", b = "") {
const statusObj = checkNumber(a, b);
if (!statusObj.status) {
return statusObj.data;
} else {
const tagA = Number(a) < 0,
tagB = Number(b) < 0;
const strA = `${a}`,
strB = `$`;
const lenA = tagA ? strA.length - 1 : strA.length;
const lenB = tagB ? strB.length - 1 : strB.length;
const maxLen = Math.max(lenA, lenB);
const padLen = Math.ceil(maxLen / intLen) * intLen; // 即為會(huì)用到的整個(gè)數(shù)組長(zhǎng)度
const newA = tagA
? `-${strA.slice(1).padStart(padLen, "0")}`
: strA.padStart(padLen, "0");
const newB = tagB
? `-${strB.slice(1).padStart(padLen, "0")}`
: strB.padStart(padLen, "0");
let result = IntCalc(newA, newB);
// 去掉正負(fù)數(shù)前面無(wú)意義的字符 ‘0'
const numberResult = Number(result);
if (numberResult > 0) {
while (result[0] === "0") {
result = result.slice(1);
}
} else if (numberResult < 0) {
while (result[1] === "0") {
result = "-" + result.slice(2);
}
} else {
result = "0";
}
return result;
}
}
function IntCalc(a, b) {
let result = "0";
const intA = Number(a),
intB = Number(b);
// 判斷是否為安全數(shù),不為安全數(shù)的操作進(jìn)入復(fù)雜計(jì)算模式
if (
isSafeNumber(intA) &&
isSafeNumber(intB) &&
isSafeNumber(intA + intB)
) {
result = `${intA + intB}`;
} else {
const sliceA = a.slice(1),
sliceB = b.slice(1);
if (a[0] === "-" && b[0] === "-") {
// 兩個(gè)數(shù)都為負(fù)數(shù),取反后計(jì)算,結(jié)果再取反
result = "-" + calc(sliceA, sliceB, true);
} else if (a[0] === "-") {
// 第一個(gè)數(shù)為負(fù)數(shù),第二個(gè)數(shù)為正數(shù)的情況
const newV = compareNumber(sliceA, b);
if (newV === 1) {
// 由于 a 的絕對(duì)值比 b 大,為了確保返回結(jié)果為正數(shù),a的絕對(duì)值作為第一個(gè)參數(shù)
result = "-" + calc(sliceA, b, false);
} else if (newV === -1) {
// 道理同上
result = calc(b, sliceA, false);
}
} else if (b[0] === "-") {
// 第一個(gè)數(shù)為正數(shù),第二個(gè)數(shù)為負(fù)數(shù)的情況
const newV = compareNumber(sliceB, a);
if (newV === 1) {
// 由于 b 的絕對(duì)值比 a 大,為了確保返回結(jié)果為正數(shù),b的絕對(duì)值作為第一個(gè)參數(shù)
result = "-" + calc(sliceB, a, false);
} else if (newV === -1) {
// 道理同上
result = calc(a, sliceB, false);
}
} else {
// 兩個(gè)數(shù)都為正數(shù),直接計(jì)算
result = calc(a, b, true);
}
}
return result;
}
/**
* @Description: 比較兩個(gè)整數(shù)字符串是否正確
* @param { string }: 比較的第一個(gè)整數(shù)字符串
* @param { string }: 比較的第一個(gè)整數(shù)字符串
* @return { object }: 返回是否要退出函數(shù)的狀態(tài)和退出函數(shù)返回的數(shù)據(jù)
*/
function checkNumber(a, b) {
const obj = {
status: true,
data: null
};
const typeA = typeof a,
typeB = typeof b;
const allowTypes = ["number", "string"];
if (!allowTypes.includes(typeA) || !allowTypes.includes(typeB)) {
console.error("參數(shù)中存在非法的數(shù)據(jù),數(shù)據(jù)類型只支持 number 和 string");
obj.status = false;
obj.data = false;
}
if (Number.isNaN(a) || Number.isNaN(b)) {
console.error("參數(shù)中不應(yīng)該存在 NaN");
obj.status = false;
obj.data = false;
}
const intA = Number(a),
intB = Number(b);
if (intA === 0) {
obj.status = false;
obj.data = b;
}
if (intB === 0) {
obj.status = false;
obj.data = a;
}
const inf = [Infinity, -Infinity];
if (inf.includes(intA) || inf.includes(intB)) {
console.error("參數(shù)中存在Infinity或-Infinity");
obj.status = false;
obj.data = false;
}
return obj;
}
/**
* @Description: 比較兩個(gè)整數(shù)字符串正負(fù)
* @param { string } a 比較的第一個(gè)整數(shù)字符串
* @param { string } b 比較的第二個(gè)整數(shù)字符串
* @return { boolean } 返回第一個(gè)參數(shù)與第二個(gè)參數(shù)的比較
*/
function compareNumber(a, b) {
if (a === b) return 0;
if (a.length > b.length) {
return 1;
} else if (a.length < b.length) {
return -1;
} else {
for (let i = 0; i < a.length; i++) {
if (a[i] > b[i]) {
return 1;
} else if (a[i] < b[i]) {
return -1;
}
}
}
}
/**
* @Description: 相加的結(jié)果
* @param { string } a 相加的第一個(gè)整數(shù)字符串
* @param { string } b 相加的第二個(gè)整數(shù)字符串
* @param { string } type 兩個(gè)參數(shù)是 相加(true) 還是相減(false)
* @return { string } 返回相加的結(jié)果
*/
function calc(a, b, type = true) {
const arr = []; // 保存每個(gè)部分計(jì)算結(jié)果的數(shù)組
for (let i = 0; i < a.length; i += intLen) {
// 每部分長(zhǎng)度 15 的裁取字符串
const strA = a.slice(i, i + intLen);
const strB = b.slice(i, i + intLen);
const newV = Number(strA) + Number(strB) * (type ? 1 : -1); // 每部分的計(jì)算結(jié)果,暫時(shí)不處理
arr.push(`${newV}`);
}
let num = ""; // 連接每個(gè)部分的字符串
for (let i = arr.length - 1; i >= 0; i--) {
if (arr[i] > 0) {
// 每部分結(jié)果大于 0 的處理方案
const str = `${arr[i]}`;
if (str.length < intLen) {
// 長(zhǎng)度不足 15 的首部補(bǔ)充字符‘0'
num = str.padStart(intLen, "0") + num;
} else if (str.length > intLen) {
// 長(zhǎng)度超過(guò) 15 的扔掉第一位,下一部分進(jìn)位加一
num = str.slice(1) + num;
if (i >= 1 && str[0] !== "0") arr[i - 1]++;
else num = "1" + num;
} else {
// 長(zhǎng)度等于 15 的直接計(jì)算
num = str + num;
}
} else if (arr[i] < 0) {
// 每部分結(jié)果小于 0 的處理方案,借位 10的15次方計(jì)算,結(jié)果恒為正數(shù),首部填充字符‘0'到15位
const newV = `${10 ** intLen + Number(arr[i])}`;
num = newV.padStart(intLen, "0") + num;
if (i >= 1) arr[i - 1]--;
} else {
// 每部分結(jié)果等于 0 的處理方案,連續(xù)15個(gè)字符‘0'
num = "0".padStart(intLen, "0") + num;
}
}
return num;
}測(cè)試結(jié)果:
全部代碼請(qǐng)點(diǎn)擊 這里
console.log(MAX) // 9007199254740991
intAdd(MAX, '2') // '9007199254740993'
intAdd(MAX, '10000000000000000') // '19007199254740991'
// 下面測(cè)試10的二十一次方的數(shù)據(jù) 1000000000000000000000
intAdd(MAX, '1000000000000000000000') // '1000009007199254740991'
intAdd(MAX, `-${10 ** 16}`) // '-992800745259009'
// 仍然存在一個(gè)問(wèn)題,就是不要使用計(jì)算中的字符串,如下
intAdd(MAX, `${10 ** 21}`) // '10.0000000071992548e+21'
intAdd(MAX, `-${10 ** 21}`) // '0'
轉(zhuǎn)換科學(xué)計(jì)算
當(dāng)然考慮到由于一般計(jì)算不會(huì)使用大數(shù),書(shū)寫(xiě)字符串相加確實(shí)感覺(jué)怪怪的,可以在函數(shù)內(nèi)加入判斷,是科學(xué)計(jì)數(shù)法的提示并轉(zhuǎn)換為10進(jìn)制數(shù),進(jìn)行代碼改進(jìn):
/**
* @Description: 計(jì)算兩個(gè)數(shù)之和,返回計(jì)算結(jié)果
* @param { String }: a 相加的第一個(gè)整數(shù)字符串
* @param { String }: b 相加的第一個(gè)整數(shù)字符串
* @return { string }: 返回計(jì)算結(jié)果
*/
function intAdd(a = "", b = "") {
const statusObj = checkNumber(a, b);
if (!statusObj.status) {
return statusObj.data;
} else {
let newA, newB, maxLen;
const tagA = Number(a) < 0,
tagB = Number(b) < 0;
let strA = `${a}`,
strB = `$`;
const reg = /^\-?(\d+)(\.\d+)?e\+(\d+)$/;
if (reg.test(a) || reg.test(b)) {
console.warn(
"由于存在科學(xué)計(jì)數(shù)法,計(jì)算結(jié)果不一定準(zhǔn)確,請(qǐng)轉(zhuǎn)化成字符串后計(jì)算"
);
strA = strA.replace(reg, function(...rest) {
const str = rest[2] ? rest[1] + rest[2].slice(1) : rest[1];
return str.padEnd(Number(rest[3]) + 1, "0");
});
strB = strB.replace(reg, function(...rest) {
const str = rest[2] ? rest[1] + rest[2].slice(1) : rest[1];
return str.padEnd(Number(rest[3]) + 1, "0");
});
maxLen = Math.max(a.length, b.length);
} else {
const lenA = tagA ? strA.length - 1 : strA.length;
const lenB = tagB ? strB.length - 1 : strB.length;
maxLen = Math.max(lenA, lenB);
}
const padLen = Math.ceil(maxLen / intLen) * intLen; // 即為會(huì)用到的整個(gè)數(shù)組長(zhǎng)度
newA = tagA
? `-${strA.slice(1).padStart(padLen, "0")}`
: strA.padStart(padLen, "0");
newB = tagB
? `-${strB.slice(1).padStart(padLen, "0")}`
: strB.padStart(padLen, "0");
let result = intCalc(newA, newB);
// 去掉正負(fù)數(shù)前面無(wú)意義的字符 ‘0'
const numberResult = Number(result);
if (numberResult > 0) {
while (result[0] === "0") {
result = result.slice(1);
}
} else if (numberResult < 0) {
while (result[1] === "0") {
result = "-" + result.slice(2);
}
} else {
result = "0";
}
console.log(result);
return result;
}
}解決整數(shù)減法的坑
加法和減法同理,只需要把第二個(gè)參數(shù)取反后利用加法運(yùn)算就可以了,由于之前已經(jīng)提取了模板,可以直接定義減法函數(shù)
/**
* @Description: 整數(shù)減法函數(shù)入口
* @param { String }: a 減法的第一個(gè)整數(shù)字符串
* @param { String }: b 減法的第一個(gè)整數(shù)字符串
* @return { string }: 返回計(jì)算結(jié)果
*/
function intSub(a = "0", b = "0") {
const newA = `${a}`;
const newB = Number(b) > 0 ? `-$` : `$`.slice(1);
const result = intAdd(newA, newB);
return result;
}
測(cè)試結(jié)果
全部代碼請(qǐng)點(diǎn)擊 這里
intSub('9037499254750994', '-9007299251310995')
// 18044798506061989
解決小數(shù)加法的坑
文章開(kāi)頭說(shuō)了,小數(shù)加減,可以通過(guò)浮點(diǎn)進(jìn)行計(jì)算,但是這里既然完成了整數(shù)的加減,那么能不能利用整數(shù)的加減原理來(lái)解決小數(shù)的加減計(jì)算呢?
- 整數(shù)加法代碼中經(jīng)常出現(xiàn)
padStart這個(gè)向前補(bǔ)齊的函數(shù),因?yàn)樵谡麛?shù)前加字符‘0’的對(duì)本身沒(méi)有影響。 - 小數(shù)也有這個(gè)原理,往尾部補(bǔ)‘0’同樣對(duì)小數(shù)沒(méi)有影響,然后再補(bǔ)齊后的數(shù)通過(guò)整數(shù)加減來(lái)計(jì)算。
首先來(lái)看下小數(shù)的加法計(jì)算實(shí)現(xiàn)
/**
* @Description: 小數(shù)加法函數(shù)入口
* @param { String }: a 相加的第一個(gè)整數(shù)字符串
* @param { String }: b 相加的第一個(gè)整數(shù)字符串
* @return { string }: 返回計(jì)算結(jié)果
*/
function floatAdd(a = "0", b = "0") {
const statusObj = checkNumber(a, b);
if (!statusObj.status) {
return statusObj.data;
} else {
const strA = `${a}`.split("."),
strB = `$`.split(".");
let newA = strA[1],
newB = strB[1];
const maxLen = Math.max(newA.length, newB.length);
const floatLen = Math.ceil(maxLen / intLen) * intLen;
newA = newA.padEnd(floatLen, "0");
newB = newB.padEnd(floatLen, "0");
newA = strA[0][0] === "-" ? `-${newA}` : newA;
newB = strB[0][0] === "-" ? `-${newB}` : newB;
let result = intCalc(newA, newB);
let tag = true,
numResult = Number(result);
// 去掉正負(fù)數(shù)后面無(wú)意義的字符 ‘0'
if (numResult !== 0) {
if (numResult < 0) {
result = result.slice(1);
tag = false;
}
result =
result.length === floatLen ? `0.${result}` : `1.${result.slice(1)}`;
result = tag ? result : `-${result}`;
let index = result.length - 1;
while (result[index] === "0") {
result = result.slice(0, -1);
index--;
}
} else {
result = "0";
}
console.log(result);
return result;
}
}測(cè)試結(jié)果
floatAdd('0.9037499254750994', '-0.9007299251310995')
// 0.0030200003439999
解決小數(shù)減法的坑
與整數(shù)減法的原理相同,可以直接定義減法函數(shù)
/**
* @Description: 小數(shù)減法函數(shù)入口
* @param { String }: a 相減的第一個(gè)整數(shù)字符串
* @param { String }: b 相減的第一個(gè)整數(shù)字符串
* @return { string }: 返回計(jì)算結(jié)果
*/
function floatSub(a = '0', b = '0') {
const newA = `${a}`
const newB = Number(b) > 0 ? `-$`: `${b.slice(1)}`
const result = floatAdd(newA, newB)
return result
}
測(cè)試結(jié)果
全部代碼請(qǐng)點(diǎn)擊 這里
floatSub('0.9037499254750994', '-0.9007299251310995')
// 1.8044798506061989
解決整數(shù)加小數(shù)的通用問(wèn)題
其實(shí)在實(shí)際開(kāi)發(fā)過(guò)程中,并不是整數(shù)相加減,小數(shù)相加減,都有可能出現(xiàn),所以還要考慮整數(shù)與小數(shù)之間的加減計(jì)算
這里的解決思路仍然是往前補(bǔ)0和往后補(bǔ)0
把整數(shù)和小數(shù)都補(bǔ)充完整后,合在一起進(jìn)行整數(shù)相加
最后根據(jù)之前保存的整數(shù)的長(zhǎng)度,插入小數(shù)點(diǎn)
剩下的就是把無(wú)意義的0排除掉,輸出結(jié)果
/**
* @Description: 計(jì)算兩個(gè)數(shù)之差,返回計(jì)算結(jié)果
* @param { String }: a 相減的第一個(gè)整數(shù)字符串
* @param { String }: b 相減的第一個(gè)整數(shù)字符串
* @return { string }: 返回計(jì)算結(jié)果
*/
function allSub(a = "0", b = "0") {
const newA = `${a}`;
const newB = Number(b) > 0 ? `-$` : `$`.slice(1);
const result = allAdd(newA, newB);
return result;
}
/**
* @Description: 計(jì)算兩個(gè)數(shù)之和,返回計(jì)算結(jié)果
* @param { String }: a 相加的第一個(gè)整數(shù)字符串
* @param { String }: b 相加的第一個(gè)整數(shù)字符串
* @return { string }: 返回計(jì)算結(jié)果
*/
function allAdd(a = "0", b = "0") {
const statusObj = checkNumber(a, b);
if (!statusObj.status) {
return statusObj.data;
} else {
const strA = `${a}`.split("."),
strB = `$`.split(".");
let intAs = strA[0],
floatA = strA.length === 1 ? "0" : strA[1];
let intBs = strB[0],
floatB = strB.length === 1 ? "0" : strB[1];
// 可能存在純整數(shù) 或者純小數(shù) 0.xxxxxxx
const tagA = intAs > 0 || !intAs[0] === '-' || intAs[0] === '0',
tagB = intBs > 0 || !intBs[0] === '-' || intBs[0] === '0';
const maxIntLen = Math.max(intAs.length, intBs.length);
const arrIntLen = Math.ceil(maxIntLen / intLen) * intLen;
const maxFloatLen = Math.max(floatA.length, floatB.length);
const arrFloatLen = Math.ceil(maxFloatLen / intLen) * intLen;
intAs = tagA
? intAs.padStart(arrIntLen, "0")
: intAs.slice(1).padStart(arrIntLen, "0");
intBs = tagB
? intBs.padStart(arrIntLen, "0")
: intBs.slice(1).padStart(arrIntLen, "0");
let newA =
floatA === "0"
? intAs + "0".padEnd(arrFloatLen, "0")
: intAs + floatA.padEnd(arrFloatLen, "0");
let newB =
floatB === "0"
? intBs + "0".padEnd(arrFloatLen, "0")
: intBs + floatB.padEnd(arrFloatLen, "0");
newA = tagA ? newA : `-${newA}`;
newB = tagB ? newB : `-${newB}`;
let result = intCalc(newA, newB);
const numResult = Number(result);
if (result.length > arrIntLen) {
result = result.slice(0, -arrFloatLen) + "." + result.slice(arrFloatLen);
}
// 去掉正負(fù)數(shù)前面后面無(wú)意義的字符 ‘0'
if (numResult !== 0) {
if (numResult > 0) {
while (result[0] === "0") {
result = result.slice(1);
}
} else if (numResult < 0) {
while (result[1] === "0") {
result = "-" + result.slice(2);
}
result = result.slice(1);
tag = false;
}
let index = result.length - 1;
while (result[index] === "0") {
result = result.slice(0, -1);
index--;
}
} else {
result = "0";
}
if (result[result.length - 1] === ".") {
result = result.slice(0, -1);
}
if (result[0] === ".") {
result = "0" + result;
}
console.log(result);
return result;
}
}測(cè)試結(jié)果
全部代碼請(qǐng)點(diǎn)擊 這里
allAdd("9037499254750994", "0.9007299251310995");
// 9037499254750994.9007299251310995
allSub("9037499254750994", "-0.9007299251310995");
// 9037499254750994.9007299251310995
allAdd('9037499254750994.9037499254750994', '-9007299251310995.9007299251310995');
// 30200003439999.0030200003439999
allSub('9037499254750994.9037499254750994', '9007299251310995.9007299251310995');
// 30200003439999.0030200003439999
總結(jié)
Number.MAX_SAFE_INTEGER 和 Number.MIN_SAFE_INTEGER 之間的計(jì)算才是可信任的
小數(shù)加減的浮點(diǎn)精度問(wèn)題轉(zhuǎn)移到整數(shù)來(lái)解決
超大的數(shù)加減的時(shí)候,分區(qū)計(jì)算(理由是第1點(diǎn))
拆分成每部分15長(zhǎng)度的字符串(理由是Number.MAX_SAFE_INTEGER的長(zhǎng)度為16,無(wú)論如何加減都是滿足第一點(diǎn)的,這樣就不需要去注意加減的安全性問(wèn)題了)
科學(xué)計(jì)數(shù)法的問(wèn)題,匹配是否為科學(xué)計(jì)數(shù)法的數(shù),然后轉(zhuǎn)換成十進(jìn)制,同時(shí)提出警告,因?yàn)榭茖W(xué)計(jì)數(shù)法的數(shù)存在誤差,計(jì)算會(huì)存在不準(zhǔn)確性
以上就是詳解JavaScript中任意兩數(shù)加減的解決方案的詳細(xì)內(nèi)容,更多關(guān)于JavaScript數(shù)字加減的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
微信小程序視圖控件與bindtap之間的問(wèn)題的解決
這篇文章主要介紹了微信小程序視圖控件與bindtap之間的問(wèn)題的解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
jsonp實(shí)現(xiàn)百度下拉框功能的方法分析
這篇文章主要介紹了jsonp實(shí)現(xiàn)百度下拉框功能的方法,結(jié)合實(shí)例形式分析了調(diào)用百度接口jsonp實(shí)現(xiàn)跨域請(qǐng)求及內(nèi)容渲染相關(guān)操作技巧,需要的朋友可以參考下2019-05-05
JavaScript實(shí)現(xiàn)兩個(gè)Table固定表頭根據(jù)頁(yè)面大小自行調(diào)整
正如標(biāo)題所言兩個(gè)Table固定表頭,可根據(jù)頁(yè)面大小自行調(diào)整使用JavaScript實(shí)現(xiàn),具體的示例如下,感興趣的朋友可以參考下2014-01-01
javascript中的深復(fù)制詳解及實(shí)例分析
這篇文章主要介紹了javascript中的深復(fù)制詳解及實(shí)例分析的相關(guān)資料,需要的朋友可以參考下2016-12-12
使用JavaScript實(shí)現(xiàn)一個(gè)簡(jiǎn)單的哈希映射功能
哈希表大家應(yīng)該都經(jīng)常用到吧,那么大家有沒(méi)有想過(guò)哈希表是怎么實(shí)現(xiàn)的呢,本文我們就來(lái)從一道簡(jiǎn)單的題目來(lái)了解一下哈希表的簡(jiǎn)單原理和實(shí)現(xiàn)吧2024-02-02

