上面的一些测试用例没有意义(因为我不应该期望输出等于输入,因为我使用的是.toFixed(
).
然后真正的答案是建议 https://stackoverflow.com/questions/73832451/how-to-divide-numbers-in-javascript-to-arbitrary-precision-e-g-28-decimal-plac#comment130371861_73832451通过@James:使用maxDecimal
选项:https://www.npmjs.com/package/exact-math#the-config-maxdecimal-property-usage https://www.npmjs.com/package/exact-math#the-config-maxdecimal-property-usage Or https://mikemcl.github.io/decimal.js/# precision https://mikemcl.github.io/decimal.js/#precision在decimal.js中。
请参阅下面带有 ⭐ 的行。
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/a/68906367/是什么启发了我的代码。