这是此SO question
的扩展我做了一个功能,看看我是否可以正确格式化任何数字。以下答案适用于https://regex101.com和https://regexr.com/等工具,但不在我的函数中(在节点和浏览器中尝试过): const
const format = (num, regex) => String(num).replace(regex, '$1')
基本上给出任何整数,它不应超过15位有效数字。给定任何小数,它不应超过2个小数点。
...这样 现在
format(0.12345678901234567890, /^\d{1,13}(\.\d{1,2}|\d{0,2})$/)
返回0.123456789012345678而不是0.123456789012345
但
format(0.123456789012345,/^-?(\d*\.?\d{0,2}).*/)
按预期返回格式化为2个小数点的数字。
答案 0 :(得分:2)
让我试着解释一下发生了什么。
对于给定的输入0.12345678901234567890
和正则表达式/^\d{1,13}(\.\d{1,2}|\d{0,2})$/
,让我们一步一步地看看发生了什么。
^\d{1,13}
确实匹配字符串0
(\.
现在您已经开设了一个新组,它确实匹配.
\d{1,2}
确实找到了数字1
和2
|\d{0,2}
因此跳过此部分)
所以这是捕获组的结束。$
这表示字符串的结尾,但它不会匹配,因为您还剩下345678901234567890
。Javascript返回整个字符串,因为匹配最终失败。
让我们在最后删除$
,成为/^\d{1,13}(\.\d{1,2}|\d{0,2})/
你回来".12345678901234567890"
。这会产生一些问题。
为什么前面的0
被删除?
因为它不是您的匹配组的一部分,附带()
。
为什么我们只得到两位小数,即.12
?
请记住,您正在进行replace
。这意味着默认情况下,原始字符串将保留在原位,只有匹配的部分才会被替换。由于345678901234567890
不是比赛的一部分,因此保持不变。唯一匹配的部分是0.12
。
答案 1 :(得分:0)
对标题问题的回答:你的函数没有替换,因为没有什么可以替换 - 正则表达式与字符串中的任何内容都不匹配。 csb的答案解释了所有细节。
现在,好像你有一个XY problem。你问为什么你对.replace()
的电话不起作用,但.replace()
绝对不是你应该使用的功能。 .replace()
的角色正在替换部分字符串,而您实际上想要创建不同的字符串。此外,在评论中,您建议您的格式不仅用于向用户呈现数据,而且还打算在进一步计算中使用它。您还提到了cryptocurriencies。
让我们一个接一个地解决这些问题。
好吧,只需生成您需要的字符串,而不是替换您不喜欢的字符串中的内容。有一些边缘情况。而不是编写一体化正则表达式,只需逐个处理它们。
以下代码绝对不是最好的,但它的主要目的是简单并准确显示正在发生的事情。
function format(n) {
const max_significant_digits = 15;
const max_precision = 2;
let digits_before_decimal_point;
if (n < 0) {
// Don't count minus sign.
digits_before_decimal_point = n.toFixed(0).length - 1;
} else {
digits_before_decimal_point = n.toFixed(0).length;
}
if (digits_before_decimal_point > max_significant_digits) {
throw new Error('No good representation for this number');
}
const available_significant_digits_for_precision =
Math.max(0, max_significant_digits - digits_before_decimal_point);
const effective_max_precision =
Math.min(max_precision, available_significant_digits_for_precision);
const with_trailing_zeroes = n.toFixed(effective_max_precision);
// I want to keep the string and change just matching part,
// so here .replace() is a proper method to use.
const withouth_trailing_zeroes = with_trailing_zeroes.replace(/\.?0*$/, '');
return withouth_trailing_zeroes;
}
因此,您可以按照自己的方式格式化数字。现在怎么办?
嗯,您可以将其显示给用户。而这主要是它。该值四舍五入到(1)表示它在不同的基础上,(2)适合有限的精度,因此它对于任何计算都是无用的。而且,顺便说一句,如果你想要的是一个数字,为什么你首先将它转换为String
?
嗯,这是最严重的问题。因为,你知道,floating point numbers are tricky。并且they are absolutely abysmal for representing money。因此,很可能您尝试格式化的数字已经是错误的数字。
定点算术是最明显的答案。大部分时间都有效。但是,它在JS中相当棘手,数字几乎可以随时进入浮点表示。所以,use decimal arithmetic library更好。 (可选)切换到具有内置bignums和小数的语言,如Python。