在0.5时向上或向下取整

时间:2019-03-11 06:05:46

标签: javascript math rounding precision

我遇到的问题是Javascript在达到0.5时舍入数字的方式。 我正在编写征费计算器,并注意到结果相差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"

问题:

  • 这种不稳定的路由到底是怎么回事?
  • 获得“四舍五入”结果(达到0.5时)的最快方法是什么?

5 个答案:

答案 0 :(得分:1)

这在大多数情况下都有效。 (请参见下面的注释。)

  

通过使用表示的数字可以避免舍入问题   指数符号:

@Bean
public CorsFilter corsFilter() {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    final CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("OPTIONS");
    config.addAllowedMethod("HEAD");
    config.addAllowedMethod("GET");
    config.addAllowedMethod("PUT");
    config.addAllowedMethod("POST");
    config.addAllowedMethod("DELETE");
    config.addAllowedMethod("PATCH");
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
}

位于http://www.jacklmoore.com/notes/rounding-in-javascript/

注意::正如Mark Dickinson所指出的那样,这不是一个通用的解决方案,因为在某些情况下,例如function round(value, decimals) { return Number(Math.round(value+'e'+decimals)+'e-'+decimals); } console.log(round(21480.105, 2).toFixed(2));,它会返回NaN且输入较大。 欢迎进行修改以使其更强大。

答案 1 :(得分:1)

因此,正如其他一些人已经解释的那样,“不规则”舍入的原因是浮点精度问题。您可以使用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中神奇消失/出现的货币敏感,所以您最好将货币计数器保持在整数“分”(或其他一些经过深思熟虑的货币)中结构),而不是浮点“美元”。

答案 2 :(得分:0)

您可以四舍五入为整数,然后在显示时以逗号移动:

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
}

答案 3 :(得分:0)

原因-

您可能已经听说,在某些语言(例如JavaScript)中,带小数部分的数字称为浮点数,而浮点数与数字运算的近似处理有关。不精确的计算,近似值。因为您希望通过精确的计算来精确地计算和存储2的1/3或平方根?

如果没有,那么现在您已经听说过。

这意味着当您键入数字文字21480.105时,最终存储在计算机内存中的实际值实际上不是21480.105,而是其近似值。最接近21480.105的值,可以表示为浮点数。

并且由于该值并非完全准确 21480.105,这意味着它要么比该值稍微更多,要么比该值稍微更少。如预期的那样,将对更多的部分进行四舍五入,而对更少的部分进行四舍五入。

解决方案-

您的问题来自近似值,似乎您负担不起。解决方案是使用确切的数字而不是近似的数字。

使用整数。那些是正确的。将数字转换为字符串时,请添加小数点。

答案 4 :(得分:0)

  

这种不稳定的路由到底是怎么回事?

请参考警告Mozilla Doc,该警告标识了这些差异的原因。 “浮点数不能精确地用二进制表示所有小数,这会导致意外的结果...”

另外,请参考Is floating point math broken?(感谢罗比·科尼利森的参考)

  

获得“四舍五入”结果(达到0.5时)的最快方法是什么?

使用accounting.js之类的JS库来舍入,格式化和呈现货币。

例如...

function roundToNearestCent(rawValue) {
  return accounting.toFixed(rawValue, 2);
}

const roundedValue = roundToNearestCent(21480.105);
console.log(roundedValue);
<script src="https://combinatronics.com/openexchangerates/accounting.js/master/accounting.js"></script>

此外,请考虑签出BigDecimal in JavaScript

希望有帮助!