将数字截断为两位小数而不四舍五入。

302

假设我有一个值为15.7784514,我想将其显示为15.77,而不进行四舍五入。

var num = parseFloat(15.7784514);
document.write(num.toFixed(1)+"<br />");
document.write(num.toFixed(2)+"<br />");
document.write(num.toFixed(3)+"<br />");
document.write(num.toFixed(10));

结果是 -

15.8
15.78
15.778
15.7784514000 

如何显示15.77?


83
在展示货币时,这很有用。 - Richard Ye
2
我不知道为什么你想在浮点数上运行 parseFloat。它已经是一个浮点数,所以不需要解析它。那么,你最初是有一个 string 还是一个 number - user202729
10
@CamiloMartin,1.49亿美元和10亿美元的债务之间有很大的差距。 - Broda Noel
14
1.49亿美元和1.50亿美元之间存在很大的差别,相差1亿美元。 - Liga
4
如果你将14.9亿美元四舍五入,有可能会告诉别人他们有15亿美元。这会让他们在结账时感到失望,并不得不从购物车中移除价值1.5亿美元的“Eclipse”豪华游艇。 - Rob
显示剩余3条评论
43个回答

283

将数字转换为字符串,匹配到小数点后第二位:

function calc(theform) {
    var num = theform.original.value, rounded = theform.rounded
    var with2Decimals = num.toString().match(/^-?\d+(?:\.\d{0,2})?/)[0]
    rounded.value = with2Decimals
}
<form onsubmit="return calc(this)">
Original number: <input name="original" type="text" onkeyup="calc(form)" onchange="calc(form)" />
<br />"Rounded" number: <input name="rounded" type="text" placeholder="readonly" readonly>
</form>

toFixed 方法在某些情况下会出现错误,而 toString 不会出现这种情况,因此使用时要非常小心。


26
+1 这应该解决四舍五入的问题。但我仍然建议使用 toFixed 输出上面的结果,如下所示:(Math.floor(value * 100) / 100).toFixed(2)。你可能知道,浮点数会出现精度问题(如果数字是 15 的话,似乎 OP 想要的是 15.00)。 - T.J. Crowder
33
如果value = 4.27,Math.floor(value * 100) / 100无法正常工作。确实,你可以试试看。 - Dark Hippo
4
这是因为4.27实际上是4.26999999999,虽然Math.round通常是更好的解决方案,但并不是对原问题的答案。实际上,考虑到输出是一个字符串,应该使用字符串操作而不是数值解决方案,以避免浮点数问题。 - BJury
3
“that doesn't sound right. Converting between types under any circumstances seems roundabout and unsafe. Unless a really advanced programmer would argue that "all types are just streams" or something.” “这听起来不太对。在任何情况下转换类型似乎都是迂回且不安全的。除非有一个非常高级的程序员会争辩说“所有类型只是流”之类的话。” - JackHasaKeyboard
2
@AnubhavGupta 如果要使其在小数点前没有值的情况下工作,只需将正则表达式中的+更改为*,这样您就可以得到:match(/^-?\d*(?:\.\d{0,2})?/) - Bart Enkelaar
显示剩余9条评论

104

更新于2016年11月5日

新答案,始终准确

function toFixed(num, fixed) {
    var re = new RegExp('^-?\\d+(?:\.\\d{0,' + (fixed || -1) + '})?');
    return num.toString().match(re)[0];
}

作为JavaScript中的浮点数运算总会有边缘情况,以前的解决方案大多数情况下是准确的,但并不够好。 有一些解决方案,如num.toPrecisionBigDecimal.jsaccounting.js。 然而,我认为仅仅解析字符串将是最简单和始终准确的方法。 基于@Gumbo的答案中写得很好的正则表达式,这个新的toFixed函数将始终按预期工作。

旧答案,不总是准确的。

自己编写toFixed函数:

function toFixed(num, fixed) {
    fixed = fixed || 0;
    fixed = Math.pow(10, fixed);
    return Math.floor(num * fixed) / fixed;
}

8
toFixed(4.56, 2) = 4.55 - cryss
3
许多答案在Math.floor()函数内使用了乘法运算,这样会使一些数字计算失败!浮点数运算不够准确,需要使用其他方法。 - Nico Westerdale
1
函数 toFixedCustom(num, fixed) { var re = new RegExp('^-?\d+(?:.\d{0,' + (fixed || -1) + '})?'); if(num.toString().match(re) ){ return num.toString().match(re)[0]; } } - Ryan Augustine
9
更新后的答案会失败,当数字很大或很小时,JavaScript 会用科学计数法表示。toFixed(0.0000000052, 2) 的结果为 '5.2'。可以通过使用 return num.toFixed(fixed+1).match(re)[0]; 来解决此问题。请注意,我使用了比目标位数多一位的 toFixed,以避免四舍五入,并运行了您的正则表达式匹配。 - Netherdan
1
@George,使用 New answer, always accurate toFixed(10.20, 2) 返回 10.2 - guya
显示剩余3条评论

74

另一种单行解决方案:

number = Math.trunc(number*100)/100

我用100是因为你想截取到小数点后第二位,但更灵活的解决方案是:

number = Math.trunc(number*Math.pow(10, digits))/Math.pow(10, digits)

其中digits是要保留的小数位数。

有关详细信息和浏览器兼容性,请参见Math.trunc规范


4
如果不必支持IE11,这是最佳答案。 - T. Junghans
18
抱歉,但这个方法不起作用:(4.27, 2) => 4.26,无论是在IE11还是其他浏览器中都是如此。 - ingdc
现在是2023年,Math.trunc(number*100)/100 绝对好用 - 我在经度和纬度数值上使用它(居然在 SAFARI 上,这可是新的 IE),结果非常准确。 - flaky
1
谢谢你,@ingdc!确实,它不起作用。Math.floor()也是一样的。 - undefined

50

我选择手动使用字符串方式删除余数,以避免处理数字时出现的数学问题:

num = num.toString(); //If it's not already a String
num = num.slice(0, (num.indexOf("."))+3); //With 3 exposing the hundredths place
Number(num); //If you need it back as a Number

如果num = 15.7784514,这将给你"15.77"。


8
如果您没有强制使用小数,您可能希望在遇到整数时添加条件语句。像这样:num = num.toString(); if(num.indexOf(".") > 0){num = num.slice(0, (num.indexOf("."))+3)}; Number(num); - ベンノスケ
1
如果有小数会很好,但如果是整数就会失败。 - Ruslan López
如果num是0.00000052,那么获取5.2e就会出错。 - Vasanth
2
@Vasanth 如果这个数字是0.00000052,它会返回“0.00”。也许答案已经被编辑过了。 - Drenai
是的,在边缘情况下它会失败。 - VPaul

30

更新(2021年1月)

此次更新是关于添加一个检查数字是否为科学计数法的功能,并将其转换为十进制格式。在这里,我提出了这个方案,但您可以根据应用程序的需要使用任何其他实现相同目标的函数。

function toFixed(x) {
  if (Math.abs(x) < 1.0) {
    let e = parseInt(x.toString().split('e-')[1]);
    if (e) {
        x *= Math.pow(10,e-1);
        x = '0.' + (new Array(e)).join('0') + x.toString().substring(2);
    }
  } else {
    let e = parseInt(x.toString().split('+')[1]);
    if (e > 20) {
        e -= 20;
        x /= Math.pow(10,e);
        x += (new Array(e+1)).join('0');
    }
  }
  return x;
}

现在只需将该函数应用于参数(这是与原答案相比唯一的更改):

function toFixedTrunc(x, n) {
      x = toFixed(x) 

      // From here on the code is the same than the original answer
      const v = (typeof x === 'string' ? x : x.toString()).split('.');
      if (n <= 0) return v[0];
      let f = v[1] || '';
      if (f.length > n) return `${v[0]}.${f.substr(0,n)}`;
      while (f.length < n) f += '0';
      return `${v[0]}.${f}`
    }

这个更新版本也解决了评论中提到的一个问题。
toFixedTrunc(0.000000199, 2) => "0.00" 

再次,选择最适合您应用需求的选项。

原始回答(2017年10月)

function toFixedTrunc(x, n) {
  const v = (typeof x === 'string' ? x : x.toString()).split('.');
  if (n <= 0) return v[0];
  let f = v[1] || '';
  if (f.length > n) return `${v[0]}.${f.substr(0,n)}`;
  while (f.length < n) f += '0';
  return `${v[0]}.${f}`
}

x可以是数字(将转换为字符串)或字符串。

以下是n=2的一些测试(包括OP所请求的测试):

0           => 0.00
0.01        => 0.01
0.5839      => 0.58
0.999       => 0.99
1.01        => 1.01
2           => 2.00
2.551       => 2.55
2.99999     => 2.99
4.27        => 4.27
15.7784514  => 15.77
123.5999    => 123.59

对于其他一些n的值:

15.001097   => 15.0010 (n=4)
0.000003298 => 0.0000032 (n=7)
0.000003298257899 => 0.000003298257 (n=12)

4
toFixedTrunc(0.000000199, 2) // 1.99 该代码的作用是将数字0.000000199保留两位小数并进行截断操作,最终结果为1.99。 - Maharramoff
1
SC1000,我不确定你对@Maharramoff的回答如何解释。你是说因为有关于代码有时会失败的解释,所以代码是正确的吗?还是你是在说将0.000000199截断为2位小数得到1.99是正确的结果? - Philippe-André Lorin
@SC1000 你只是在解释为什么你的方法会失败。问题在于你假设数字只有一种可能的字符串表示形式,并且这种表示形式对于0.000000199将会是“0.000000199”。如果语言不能保证这一点,那么依赖它是一个错误。 - Philippe-André Lorin
1
@Maharramoff 和其他对于 toFixedTrunc(0.000000199, 2) 感兴趣的人,请参考更新后的答案(2021年1月)。谢谢。 - ingdc
谢谢!不过,以防万一,toFixed(), 0.0000001 -> 1e-7 -> 0.00000009999999999999999,原因一般是:0.000001 * 100 ~= 0.00009999999999999999 - undefined

14

parseInt比Math.floor更快

function floorFigure(figure, decimals){
    if (!decimals) decimals = 2;
    var d = Math.pow(10,decimals);
    return (parseInt(figure*d)/d).toFixed(decimals);
};

floorFigure(123.5999)    =>   "123.59"
floorFigure(123.5999, 3) =>   "123.599"

4
它在1234567.89处失败。 - Ruslan López
你能详细说明一下吗,Lopez? - zardilior
7
floorFigure(4.56, 2) = 4.55 - cryss

12
num = 19.66752
f = num.toFixed(3).slice(0,-1)
alert(f)

这将返回19.66


15
(4.9999).toFixed(3).slice(0, -1) 返回 5.00,期望结果是 4.99。 - Bruno Lemos
3
我建议使用类似于.toFixed(6).slice(0, -4)的方法。 - Bruno Lemos
非常好,对于我的情况它可以正常工作。例如,如果我只想要一个小数,可以使用 num.toFixed(2).slice(0,-1)(如果 num 是 1.2356,则返回 1.2)。 - Esteban Perez
这很糟糕,因为如果第三个数字是9,它也会舍入第二个数字。 - Ibrahim.Sluma
js出问题了,为什么8.2 * 100等于819.99999999 - gavin

9
简单地执行这个操作。
number = parseInt(number * 100)/100;

15
parseInt(8.2*100) = 819。这段代码的意思是把 8.2 乘以 100 后得到 820,再用 parseInt() 函数将其转换成整数,所以最终结果为 819。 - VPaul
这个答案可能不是最优雅的,但它非常干净并且运行得很好。 - Michel Fernandes

8

这不是一种安全的替代方案,因为许多其他人评论了数字示例,这些数字变成指数表示法,该情况不受此函数覆盖。

// typescript
// function formatLimitDecimals(value: number, decimals: number): number {

function formatLimitDecimals(value, decimals) { 
  const stringValue = value.toString();
  if(stringValue.includes('e')) {
      // TODO: remove exponential notation
      throw 'invald number';
  } else {
    const [integerPart, decimalPart] = stringValue.split('.');
    if(decimalPart) {
      return +[integerPart, decimalPart.slice(0, decimals)].join('.')
    } else {
      return integerPart;
    }
  }
}

console.log(formatLimitDecimals(4.156, 2)); // 4.15
console.log(formatLimitDecimals(4.156, 8)); // 4.156
console.log(formatLimitDecimals(4.156, 0)); // 4
console.log(formatLimitDecimals(0, 4)); // 0

// not covered
console.log(formatLimitDecimals(0.000000199, 2)); // 0.00


8

只需截取数字:

function truncDigits(inputNumber, digits) {
  const fact = 10 ** digits;
  return Math.floor(inputNumber * fact) / fact;
}

谢谢。当然,你可以将以下内容添加到原型中: Number.prototype.truncDigits = function (digits) { const fact = 10 ** digits; return Math.floor(this.valueOf() * fact) / fact; }。 这样你就可以使用它:num.truncDigits(3) - Elihai David Vanunu
以防万一,const f = 10 ** 2; Math.floor(4.27 * f) / f; // 4.26,参考链接:https://stackoverflow.com/a/48100007/5113030 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接