我对Javascript在达到0.5时舍入数字的方式有一个问题。我正在编写levies计算器,并注意到结果中存在0.1c的差异。
问题是,他们的结果是21480.705
,我的应用程序将其转换为21480.71
,而资费显示为21480.70
。
这就是我在Javascript中看到的:
(21480.105).toFixed(2)
"21480.10"
(21480.205).toFixed(2)
"21480.21"
(21480.305).toFixed(2)
"21480.31"
(21480.405).toFixed(2)
"21480.40"
(21480.505).toFixed(2)
"21480.51"
(21480.605).toFixed(2)
"21480.60"
(21480.705).toFixed(2)
"21480.71"
(21480.805).toFixed(2)
"21480.81"
(21480.905).toFixed(2)
"21480.90"
问题:
这个不稳定的rouding?
发布于 2019-03-11 14:56:50
因此,正如其他一些人已经解释的那样,“不稳定”舍入的原因是一个浮点精度问题。您可以通过使用JavaScript编号的toExponential()
方法来调查这一点。
(21480.905).toExponential(20)
#>"2.14809049999999988358e+4"
(21480.805).toExponential(20)
#>"2.14808050000000002910e+4"
正如你在这里看到的,21480.905
得到一个比21480.905
稍小的双精度表示,而21480.805
得到一个比原始值稍大的双精度表示。由于toFixed()
方法使用双精度表示,并且不知道您最初想要的值,因此它会对它所拥有的信息做所有它能做和应该做的事情。
解决此问题的一种方法是将小数点移动到乘法所需的小数位数,然后使用标准Math.round()
,然后再次将小数点移位,无论是通过除法还是乘以逆数。最后,我们调用toFixed()
方法来确保输出值被正确地填入零。
var x1 = 21480.905;
var x2 = -21480.705;
function round_up(x,nd)
{
var rup=Math.pow(10,nd);
var rdwn=Math.pow(10,-nd); // Or you can just use 1/rup
return (Math.round(x*rup)*rdwn).toFixed(nd)
}
function round_down(x,nd)
{
var rup=Math.pow(10,nd);
var rdwn=Math.pow(10,-nd);
return (Math.round(x*-rup)*-rdwn).toFixed(nd)
}
function round_tozero(x,nd)
{
return x>0?round_down(x,nd):round_up(x,nd)
}
console.log(x1,'up',round_up(x1,2));
console.log(x1,'down',round_down(x1,2));
console.log(x1,'to0',round_tozero(x1,2));
console.log(x2,'up',round_up(x2,2));
console.log(x2,'down',round_down(x2,2));
console.log(x2,'to0',round_tozero(x2,2));
最后:遇到这样的问题通常是坐下来思考一下你是否真的使用了正确的数据类型的好时机。由于浮点误差可能会随着迭代计算而累积,而且由于人们有时对货币神奇地消失/出现在CPU中非常敏感,也许你最好将货币计数器保持在整数‘美分’(或其他一些经过深思熟虑的结构)而不是浮点‘美元’。
发布于 2019-03-11 14:47:06
为什么-
您可能听说过,在某些语言中,带有小数部分的数字调用浮点数,而浮点数用于处理数值运算的近似值。不是精确的计算,而是近似。因为你如何准确地计算和存储1/3或2的平方根,并进行精确计算?
如果你没有,那么现在你已经听说过它了。
这意味着当您键入数字文字21480.105时,最终存储在计算机内存中的实际值实际上不是21480.105,而是它的近似值。最接近21480.105的值,可以表示为浮点数。
由于此值不完全是21480.105,这意味着它要么略大于该值,要么略小于该值。正如预期的那样,更多的将被四舍五入,更少的将被四舍五入。
解决方案是-
你的问题来自于近似值,看起来你负担不起。解决方案是使用精确的数字,而不是近似的数字。
使用整数。这些都是准确的。在将数字转换为字符串时添加一个小数点。
发布于 2019-03-11 14:22:58
您可以四舍五入为Integer,然后在显示时使用逗号移位:
function round(n, digits = 2) {
// rounding to an integer is accurate in more cases, shift left by "digits" to get the number of digits behind the comma
const str = "" + Math.round(n * 10 ** digits);
return str
.padStart(digits + 1, "0") // ensure there are enough digits, 0 -> 000 -> 0.00
.slice(0, -digits) + "." + str.slice(-digits); // add a comma at "digits" counted from the end
}
https://stackoverflow.com/questions/55096050
复制相似问题