
在我們的電商前台收完訂單後,會寫入 SAP,常常會因四捨五入和SAP 進位方式不同,而導致小數進位的差異。
而然而大部份程式語言及銀行,都使用的是這種方式進位。(IEEE 754)
銀行家演算法的主要目的是確保在貨幣計算中的精度和正確性。它是一種進位方式,使用較為嚴格的規則來處理小數的進位,以減少誤差。這種演算法通常在財務系統和銀行業務中使用,因為在這些領域中,計算精度和正確性非常重要,任何微小的誤差都可能導致業務的失敗。在採用銀行家演算法的進位方式下,可以確保計算結果的精度,並且能夠在不同計算環境下保持一致。
四舍六入五考慮,五後非零就進一,五後為零看奇偶,五前為偶應舍去,五前為奇要進一
export const numberFormat = (
num: number,
decimal: number = 2, // 小數幾位
isZeroPadding: boolean = false, // 缺項補零
isNeedThousandComma: boolean = false, // 千分位
roundType = RoundType.EvenRound
) => {
try {
let result;
if (num === undefined) {
return num;
}
if (isNaN(num)) {
return num.toString();
}
let newNumber;
const sign = Math.sign(num);
// 進位
switch (roundType) {
// 四捨五入
case RoundType.Round:
newNumber = (Math.round(Math.abs(num) * Math.pow(10, decimal)) / Math.pow(10, decimal)) * sign;
break;
// 無條件進位
case RoundType.Celi:
newNumber = Math.ceil(num * Math.pow(10, decimal)) / Math.pow(10, decimal);
break;
// 無條件捨去
case RoundType.Floor:
newNumber = parseFloat(num.toFixed(decimal));
break;
case RoundType.EvenRound:
newNumber = evenRound(num, decimal);
break;
default:
newNumber = num;
}
// add Comma
let newNumberStr = newNumber.toString();
if (isZeroPadding) {
if (newNumberStr.indexOf('.') === -1) {
newNumberStr += '.';
}
const numberArray = newNumberStr.split('.');
let x1 = numberArray[0];
if (isNeedThousandComma) {
const rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + ',' + '$2');
}
}
let x2 = numberArray.length > 1 ? numberArray[1] : '';
// 缺項補零
while (x2.length < decimal) {
x2 += '0';
}
if (decimal > 0) {
x2 = '.' + x2;
}
result = x1 + x2;
return result;
}
return newNumberStr;
} catch (e) {
return num.toString();
}
};
function evenRound(num: number, decimalPlaces: number) {
const d = decimalPlaces || 0;
const m = Math.pow(10, d);
const n = +(d ? num * m : num).toFixed(8); // Avoid rounding errors
const i = Math.floor(n),
f = n - i;
const e = 1e-8; // Allow for rounding errors in f
const r = f > 0.5 - e && f < 0.5 + e ? (i % 2 === 0 ? i : i + 1) : Math.round(n);
return d ? r / m : r;
}
export const moneyFormat = (price: number) => {
return `$${numberFormat(price, 2, true, true)}`;
};
import { numberFormat, RoundType } from '@/lib/format';
test('event-round-1', () => {
const result = parseFloat(numberFormat(1.964, 2, false, false, RoundType.EvenRound));
const answer = 1.96;
expect(result).toBe(answer);
});
test('event-round-2', () => {
const result = parseFloat(numberFormat(1.9651, 2, false, false, RoundType.EvenRound));
const answer = 1.97;
expect(result).toBe(answer);
});
test('event-round-3', () => {
const result = parseFloat(numberFormat(1.965, 2, false, false, RoundType.EvenRound));
const answer = 1.96;
expect(result).toBe(answer);
});
test('event-round-4', () => {
const result = parseFloat(numberFormat(1.935, 2, false, false, RoundType.EvenRound));
const answer = 1.94;
expect(result).toBe(answer);
});
test('event-round-5', () => {
const result = parseFloat(numberFormat(1.966, 2, false, false, RoundType.EvenRound));
const answer = 1.97;
expect(result).toBe(answer);
});
相當方便,核心商業程式,點兩下就能知道有沒有把方法改壞

// 銀行家演算法進位 Math.Round(1.965,2); // 1.96 // 四捨五入 Math.Round(1.965,2, MidpointRounding.AwayFromZero,); // 1.97