我知道https://floating-point-gui.de/,也知道有许多库可以帮助处理大数,但令人惊讶的是,在除法运算的结果中,我没有找到任何处理小数点以上19位的东西。
我花了几个小时尝试一些库,如精确数学、decimal.js、bignumber.js和其他库。
您将如何处理下面用⭐标记的情况?
// https://jestjs.io/docs/getting-started#using-typescript
import exactMath from 'exact-math'; // https://www.npmjs.com/package/exact-math
import { Decimal } from 'decimal.js'; // https://github.com/MikeMcl/decimal.js
const testCases = [
// The following cases work in exact-math but fail in Decimal.js:
'9999513263875304671192000009',
'4513263875304671192000009',
'530467119.530467119',
// The following cases fail in both Decimal.js and exact-math:
'1.1998679030467029262556391239', // ⭐ exact-math rounds these 28 decimal places to 17: "1.1998679030467029263000000000"
];
describe('decimals.js', () => {
testCases.forEach((testCase) => {
test(testCase, () => {
expect(new Decimal(testCase).div(new Decimal(1)).toFixed(28)).toBe(testCase); // Dividing by 1 (very simple!)
});
});
});
describe('exact-math', () => {
testCases.forEach((testCase) => {
test(testCase, () => {
expect(exactMath.div(testCase, 1, { returnString: true })).toBe(testCase); // Dividing by 1 (very simple!)
});
});
});发布于 2022-09-23 20:46:52
上面的一些测试用例没有意义(因为我不应该期望输出与输入相等,因为我使用的是.toFixed()。
然后,真正的答案是@James:使用建议选项:https://www.npmjs.com/package/exact-math#the-config-maxdecimal-property-usage或decimal.js中的https://mikemcl.github.io/decimal.js/#precision。
请参见下面使用⭐的行。
import exactMath from 'exact-math'; // https://www.npmjs.com/package/exact-math
import { Decimal } from 'decimal.js'; // https://github.com/MikeMcl/decimal.js
/**
*
* @param amount {string}
* @param decimals {number} e.g. 6 would return 6 decimal places like 0.000000
* @param locale {string} e.g. 'en-US' or 'de-DE'
* @returns {string} e.g. 1,000.000000
*/
export function getLocaleStringToDecimals(amount: string, decimals: any, locale?: string): string {
// Thanks to https://stackoverflow.com/a/68906367/ because https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString and https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toLocaleString would not work for huge numbers or numbers with many decimal places.
const decimalFormat = new Intl.NumberFormat(locale, { minimumFractionDigits: 1, maximumFractionDigits: 1 });
const decimalFullString = '1.1';
const decimalFullNumber = Number.parseFloat(decimalFullString);
const decimalChar = decimalFormat.format(decimalFullNumber).charAt(1); // e.g. '.' or ','
const fixed = new Decimal(amount).toFixed(decimals);
const [mainString, decimalString] = fixed.split('.'); // ['321321321321321321', '357' | '998']
const mainFormat = new Intl.NumberFormat(locale, { minimumFractionDigits: 0 });
let mainBigInt = BigInt(mainString); // 321321321321321321n
const mainFinal = mainFormat.format(mainBigInt); // '321.321.321.321.321.321' | '321.321.321.321.321.322'
const decimalFinal = typeof decimalString !== 'undefined' ? `${decimalChar}${decimalString}` : ''; // '.357' | '.998'
const amountFinal = `${mainFinal}${decimalFinal}`; // '321.321.321.321.321.321,36' | '321.321.321.321.321.322,00'
// console.log({
// amount,
// fixed,
// mainString,
// decimalString,
// 'decimalString.length': decimalString ? decimalString.length : undefined,
// decimalFormat,
// decimalFinal,
// mainFormat,
// mainBigInt,
// mainFinal,
// amountFinal,
// });
return amountFinal;
}
/**
*
* @param amount {string}
* @param decimals {number} e.g. 6 would return 6 decimal places like 0.000000
* @param divisorPower {number} e.g. 0 for yocto, 24 for [base], 27 for kilo, etc
* @param locale {string} e.g. 'en-US' or 'de-DE'
* @returns {string} e.g. 1,000.000000
*/
export function round(amount: string, decimals = 0, divisorPower = 0, locale?: string): string {
if (divisorPower < 0) {
throw new Error('divisorPower must be >= 0');
}
const amountCleaned = amount.replaceAll('_', '');
const divisor = Math.pow(10, divisorPower);
const value: string = exactMath.div(amountCleaned, divisor, { returnString: true, maxDecimal: amount.length + decimals }); // ⭐ https://www.npmjs.com/package/exact-math#the-config-maxdecimal-property-usage
// console.log(`round(${amount}, decimals = ${decimals}, divisorPower = ${divisorPower}) = ${value}`, divisor);
const localeString = getLocaleStringToDecimals(value, decimals, locale);
return localeString;
}我的测试用例现在通过了。谢谢!
请参阅https://stackoverflow.com/a/68906367/,了解是什么激发了我的代码。
https://stackoverflow.com/questions/73832451
复制相似问题