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

IT技术 javascript
2021-02-01 03:25:42

假设我的值为 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?

6个回答

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

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方法在某些情况下会失败,因此要非常小心。

……这听起来不对。在任何情况下在类型之间转换似乎都是迂回和不安全的。除非一个真正高级的程序员会争辩说“所有类型都只是流”或其他什么。
2021-03-25 03:25:42
@DarkHippo 那是因为 4.27 实际上是 4.26999999999,Math.round 通常是更好的解决方案,尽管不是原始问题的答案。实际上,输出是一个字符串,这应该使用字符串操作而不是具有所有浮点弱点的数字解决方案来完成。
2021-03-27 03:25:42
@AnubhavGupta 要使其在小数点前没有值的情况下工作,只需将正则表达式中的 + 更改为 * 即可: match(/^-?\d*(?:\.\d{0,2})?/)
2021-03-30 03:25:42
+1 这应该可以解决舍入问题。不过,我仍然会使用 输出上述结果toFixed,所以:(Math.floor(value * 100) / 100).toFixed(2)正如您可能知道的那样,浮点值可能会发生奇怪的精度问题(在频谱的另一端,如果数字恰好是15,听起来就像 OP 想要的15.00)。
2021-03-31 03:25:42
Math.floor(value * 100) / 100 如果 value = 4.27 真的不行,试试吧。
2021-04-03 03:25:42

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;
}
许多这些答案在 Math.floor() 函数中都有乘法。对于某些数字,这将失败!浮点运算不准确,您需要使用不同的方法。
2021-03-11 03:25:42
toFixed(4.56, 2) = 4.55
2021-03-17 03:25:42
function toFixedCustom(num, fixed) { var re = new RegExp('^-?\\d+(?:\.\\d{0,' + (fixed || -1) + '})?'); if(num.toString().match(re)){ return num.toString().match(re)[0]; } }
2021-03-19 03:25:42
@guya,我很确定 -0.001 会返回 -0.00。我认为在大多数情况下,您需要 0 或 0.00 而不带负号。
2021-03-20 03:25:42
对于足够大或足够小的 javascript 以科学记数法表示的数字,更新的答案将失败。toFixed(0.0000000052, 2)产生'5.2'。这可以通过使用return num.toFixed(fixed+1).match(re)[0];Notice I'm using toFixed 在目标上方一位小数以避免四舍五入,然后运行您的正则表达式匹配来解决
2021-03-28 03:25:42

我选择写这个来手动删除带有字符串的余数,这样我就不必处理数字带来的数学问题:

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”;

@Vasanth 如果数字为 0.00000052,则返回“0.00”。也许从那以后答案已被编辑
2021-03-15 03:25:42
如果您不强制使用小数,则可能需要添加条件以防遇到整数。像这样: num = num.toString(); if(num.indexOf(".") > 0){num = num.slice(0, (num.indexOf("."))+3)}; 数量(数量);
2021-03-23 03:25:42
是的,它在边缘情况下失败
2021-03-27 03:25:42
如果 num 是 0.00000052,那么得到 5.2e 是错误的
2021-04-04 03:25:42
如果它有小数很好,但如果它是一个整数,它就会失败
2021-04-10 03:25:42

另一个单线解决方案:

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

我使用 100 是因为您想截断到第二个数字,但更灵活的解决方案是:

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

其中digits 是要保留的十进制数字的数量。

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

它运作良好。谢谢@T.Junghans。
2021-03-26 03:25:42
如果您不必支持 IE11,这是最佳答案。
2021-04-04 03:25:42
@T.Junghans 抱歉,但这不起作用:(4.27, 2) => 4.26 并且无论 IE11 还是任何其他浏览器都会发生这种情况。
2021-04-10 03:25:42

更新(2021 年 1 月)

根据其范围,javascript 中的数字可能会以科学记数法显示。例如,如果您在控制台中键入 0.0000001,您可能会看到它是 1e-7,而 0.000001 看起来没有变化 (0.000001)。如果您的应用程序处理一系列不涉及科学记数法的数字,您可以忽略此更新并使用下面的原始答案。

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

function toFixed(x) {
  if (Math.abs(x) < 1.0) {
    var 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 {
    var 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 月)

对于任何 n≥0,将数字截断(不四舍五入)为第 n 个十进制数字并将其转换为具有恰好 n 个十进制数字的字符串的通用解决方案。
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)
@ SC1000 您只是在解释您的方法失败的原因。问题是你假设数字只有一种可能的字符串表示,而这种表示对于 0.000000199 来说是“0.000000199”。如果语言不能保证这一点,那么依赖它就是错误的。
2021-03-16 03:25:42
@Philippe-AndréLorin 如果问题不清楚,请考虑这个函数 concatNumbers (a,b) { return a.toString() + ':' + b.toString() } 并尝试使用参数 a) (0.000001, 2 ) b) (0.0000001, 2)。a) 返回预期结果 ("0.000001:2"),b) 不返回 ("1e-7:2")。如您所见,这是一个普遍问题,并非特定于答案中的功能。
2021-03-23 03:25:42
@Maharramoff 和其他对 toFixedTrunc(0.000000199, 2) 案例感兴趣的人,请参阅更新的答案(2021 年 1 月)。谢谢。
2021-04-05 03:25:42
SC1000,我不确定如何解释您对@Maharramoff 的回答。你是说因为有解释为什么你的代码有时会失败,代码是正确的?或者您是说 1.99 是将 0.000000199 截断为 2 个小数位的正确结果?
2021-04-07 03:25:42
toFixedTrunc(0.000000199, 2) // 1.99
2021-04-09 03:25:42