toFixed()和toPrecision()的区别是什么?

165

我刚接触JavaScript,发现可以用toFixed()toPrecision()对数字进行四舍五入处理。但是,我不知道这两者之间的区别。

number.toFixed()number.toPrecision()有何区别?

9个回答

154

toFixed(n) 提供小数点后 n 位长度;toPrecision(x) 提供总长度为 x

w3schools 上的参考链接:toFixedtoPrecision

编辑:
我之前了解到 w3schools 不是最好的资源,但是直到看到 kzh 的评论之前,我忘记了这个答案。这里还有来自 Mozilla Doc Center 的其他参考链接:关于 toFixed()关于 toPrecision()。幸运的是,MDC 和 w3schools 在这个问题上达成了一致。

为了完整起见,我应该提到toFixed()等同于toFixed(0),而toPrecision()只是返回原始数字而没有进行格式化。
当然,真正的权威来源是JS规范,在这种情况下是https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-number.prototype.toprecision

12
抱歉,我于2010年7月发布了这篇文章,但直到今年才了解到w3fools。虽然Fools在某些方面是正确的,但并非schools上的所有内容都是错误的。感谢您指出我需要更新这篇文章,我会稍后进行更新。 - Pops
33
toPrecision(x)并不是“提供x的总长度”,而是按照给定的有效数字格式化数字。例如,0.0000022.toPrecision(1)将返回0.000002 - Andy E
9
我刚刚访问了 w3fools 网站,但并没有被说服。我甚至看不到任何论点,只看到了宣传另外两个网站的广告。 - NiCk Newman

73

我认为前者给出了固定的小数位数,而后者给出了固定的有效数字位数。

Math.PI.toFixed(2); // "3.14"
Math.PI.toPrecision(2); // "3.1"
此外,如果一个数字的整数位数多于指定的精度,则toPrecision将产生科学计数法
(Math.PI * 10).toPrecision(2); // "31"
(Math.PI * 100).toPrecision(2); // "3.1e+2"

编辑: 如果你是 JavaScript 初学者,我强烈推荐由道格拉斯·克罗福德所著的书籍“JavaScript: The Good Parts”。


这个答案没有解释这种行为:(0.004).toPrecision(3) //'0.000400'。我怀疑“给你固定数量的有效数字”并不是一个恰当的解释。 - Dmitry Koroliov
1
@DmitryKoroliov 我觉得这已经解释清楚了。在你的例子中,“4”这个数字是第一个有效数字,所以当你要求显示3个有效数字时,它必须添加两个零,这种情况下即为 "004"。 - Tom

35

例子很清晰:

var A = 123.456789;

A.toFixed()      // 123
A.toFixed(0)     // 123
A.toFixed(1)     // 123.5      round up last
A.toFixed(2)     // 123.46     round up last
A.toFixed(3)     // 123.457    round up last
A.toFixed(4)     // 123.4568   round up last
A.toFixed(5)     // 123.45679  round up last
A.toFixed(6)     // 123.456789
A.toFixed(7)     // 123.4567890
A.toFixed(8)     // 123.45678900
A.toFixed(9)     // 123.456789000
A.toFixed(10)    // 123.4567890000
A.toFixed(11)    // 123.45678900000

A.toPrecision()      // 123.456789 
A.toPrecision(0)     // --- ERROR --- 
A.toPrecision(1)     // 1e+2
A.toPrecision(2)     // 1.2e+2
A.toPrecision(3)     // 123
A.toPrecision(4)     // 123.5      round up last
A.toPrecision(5)     // 123.46     round up last
A.toPrecision(6)     // 123.457    round up last
A.toPrecision(7)     // 123.4568   round up last
A.toPrecision(8)     // 123.45679  round up last
A.toPrecision(9)     // 123.456789
A.toPrecision(10)    // 123.4567890
A.toPrecision(11)    // 123.45678900

// ----------------------
// edge-case rounding
// ----------------------
var B = 999.99;
B.toFixed(0)      // 1000
B.toFixed(1)      // 1000.0
B.toFixed(2)      // 999.99
B.toFixed(3)      // 999.990

B.toPrecision(0)  // --- ERROR ----
B.toPrecision(1)  // 1e+3
B.toPrecision(2)  // 1.0e+3
B.toPrecision(3)  // 1.00e+3
B.toPrecision(4)  // 1000
B.toPrecision(5)  // 999.99
B.toPrecision(6)  // 999.990

var C = 0.99;
C.toFixed(0)      // 1
C.toFixed(1)      // 1.0
C.toFixed(2)      // 0.99
C.toFixed(3)      // 0.990

C.toPrecision(0)  // --- ERROR ----
C.toPrecision(1)  // 1
C.toPrecision(2)  // 0.99
C.toPrecision(3)  // 0.990

应该包括像 999.9 toPrecision(3) -> 1.00e+3 这样的奇怪情况。这些情况不能适用于示例所展示的行为。 - Douglas Gaskell

11

我认为最好用一个例子来回答这个问题。

假设您有以下数据:

var products = [
  {
    "title": "Really Nice Pen",
    "price": 150
  },
  {
    "title": "Golf Shirt",
    "price": 49.99
  },
  {
    "title": "My Car",
    "price": 1234.56
  }
]

您想以标题和格式化价格的方式显示每个产品。首先,让我们尝试使用toPrecision

document.write("The price of " + products[0].title + " is $" + products[0].price.toPrecision(5));

The price of Really Nice Pen is $150.00

看起来不错,所以您可能认为这也适用于其他产品:

document.write("The price of " + products[1].title + " is $" + products[2].price.toPrecision(5));
document.write("The price of " + products[2].title + " is $" + products[2].price.toPrecision(5));

The price of Golf Shirt is $49.990
The price of My Car is $1234.6

不太好。我们可以通过更改每个产品的有效数字位数来解决这个问题,但是如果我们正在迭代产品数组,那可能会很棘手。让我们改用toFixed

document.write("The price of " + products[0].title + " is $" + products[0].price.toFixed(2));
document.write("The price of " + products[1].title + " is $" + products[2].price.toFixed(2));
document.write("The price of " + products[2].title + " is $" + products[2].price.toFixed(2));

The price of Really Nice Pen is $150.00
The price of Golf Shirt is $49.99
The price of My Car is $1234.56

这会得到您预期的结果,没有猜测的成分,也没有四舍五入。


6

只需:

49.99.toFixed(5)
// → "49.99000"

49.99.toPrecision(5)
// → "49.990"

5
在某些情况下,toPrecision() 会返回科学计数法,而 toFixed() 则不会。

实际上,toExponential()是一个单独的函数 - Pops
5
根据我的《JavaScript权威指南》副本,如果“精度(precision)”参数足够大以包含数字的整数部分的所有位数,则 toPrecision(precision) 将使用定点表示法。否则,将使用科学计数法。 - Robusto
在某些情况下,这是不正确的:在我的Firefox中,使用a = 999999999999999934464;a.toFixed(0)返回"1e+21"。也许更准确的答案是,除非toString()返回指数符号,否则toFixed()不会返回指数符号。 - the paul

2

toFixed(fractionDigits?)已经在其他答案中被解释得比较准确:

  • 它返回一个数字的字符串表示,小数点后有fractionDigits [位]数字:

例如:

(-3).toFixed(1)     // '-3.0'
(-3).toFixed(0)     // '-3'
(-3).toFixed()      // '-3'
(-0.03).toFixed(1)  // '-0.0'

toPrecision(precision?)在之前的回答中没有被正确描述,因此需要注意。此外,我要添加一个免责声明,即我无法理解toPrecision的规范,因此下面的内容是基于在Node.js中测试实现的尝试和错误方法得出的结论。这些步骤并不涵盖所有的边界情况,比如数字是NaN、精度参数不是整数或小于1或大于100等等。这是为了避免让解释变得混乱,因为规范似乎很复杂。

*列表中保留了案例编号,尽管某些案例可能与其他案例相似,但它们必须展示特定的行为

  1. 首先,使用precision以指数表示法表示数字,其中precision是有效数字中的位数,例如

情况1:0.000004 → 精度 = 3 → 400 * 10^-8
情况2:0.0000004 → 精度 = 3 → 400 * 10^-9
情况3:123 → 精度 = 1 → 1.23 * 10^2
情况4:153 → 精度 = 1 → 1.53 * 10^2
情况5:1234.56 → 精度 = 3 → 123.456 * 10^1
情况6:1234.56 → 精度 = 5 → 12345.6 * 10^-1

  1. 然后它将四舍五入到有效数字位数

情况1:400 * 10^-8400 * 10^-8(没有分数,没有变化)
情况2:400 * 10^-9400 * 10^-9(与情况1的推理相同)
情况3:1.23 * 10^21 * 10^21.23四舍五入为1
情况4:1.53 * 10^22 * 10^21.53四舍五入为2
情况5:123.456 * 10^1123 * 10^1(也进行了四舍五入)
情况6:12345.6 * 10^-112346 * 10^-1(四舍五入)

  1. 然后它按照以下规则生成数字的字符串表示形式:

3a) 保留步骤2中有效数字中的所有数字

3b) 如果 precision < 点前面的数字数量(在“正常”即十进制表示中),则使用指数符号表示

情况3:1 * 10^2'1e+2'(数字现在为100,有3位数字,精度为1,使用指数)
情况4:2 * 10^2'2e+2'(数字现在为200,有3位数字,精度为1,使用指数)
情况5:123 * 10^1'1.23e+3'(数字现在为1230,有4位数字,精度为3,使用指数; 请注意,有效数字从第2步中保留了所有数字123变为1.23

3c) 如果数字的整数部分为0,并且小数点后面到第一个有效数字之间的零的数量>5,则使用指数表示法。

案例2:400 * 10^-9'4.00e-7'(数字为0.0000004,它有整数部分0,小数点后面有>5个零,请注意,来自400 * 10^-9的两个零已被保留

3d) 如果数字的整数部分为0且小数点右侧的零的数量<=5,则使用十进制表示法,但将从步骤2中的有效数字中保留数字

案例1:400 * 10^-8'0.00000400'(有效数字部分的两个零已被保留)

3e) 如果精度>=十进制表示中小数点前的数字数量,则使用十进制表示法。

案例6:12346 * 10^-11234.6(步骤2中数字的小数形式为1234.6,精度为5,小数点前的数字个数为4,字符串使用十进制表示法,步骤2中的有效数字全部被保留)。

console.log((0.000004).toPrecision(3));
console.log((0.0000004).toPrecision(3));
console.log((123).toPrecision(1));
console.log((153).toPrecision(1));
console.log((1234.56).toPrecision(3));
console.log((1234.56).toPrecision(5));


这可能解释了某些奇怪的事情,比如 999.9 toPrecisison(3) 输出时会变成 1.00e+3 而不是 999 或者 1...我们该如何绕过这种情况呢? - Douglas Gaskell

1
例如,我们将变量a视为, var a = 123.45 a.toPrecision(6) 输出为123.450 a.toFixed(6) 输出类似于123.450000 //小数点后6位

0

toPrecision()toFixed()都是用于在打印数字之前格式化数字的函数。因此,它们都返回String值。

但有一个例外。如果您将这些函数用于负数数字文字,由于运算符优先级,将返回一个数字。这意味着toFixed()toPrecision()首先会返回一个字符串,然后减号运算符将该字符串转换回数字作为负值。请参见下面的示例。

toPrecision()返回一个表示Number对象的字符串,该字符串采用固定点或指数表示法舍入到有效数字。因此,如果您指定要精确到1,则它将返回第一个有效数字以及科学计数法来表示10的幂或其小数点之前的前一个0(如果有效数字<0)。

const num1 = 123.4567;

// if no arguments are passed, it is similar to converting the Number to String
num1.toPrecision();   // returns "123.4567

// scientific notation is used when you pass precision count less than total
// number of digits left of the period
num1.toPrecision(2);  // returns "1.2e+2"

// last digit is rounded if precision is less than total significant digits
num1.toPrecision(4);  // returns "123.5"
num1.toPrecision(5);  // returns "123.46"

const largeNum = 456.789;
largeNum.toPrecision(2);  // returns "4.6e+2"

// trailing zeroes are added if precision is > total digits of the number or float
num1.toPrecision(9);  // returns "123.456700"

const num2 = 123;
num2.toPrecision(4);  // returns "123.0"

const num3 = 0.00123;
num3.toPrecision(4);  // returns "0.001230"
num3.toPrecision(5);  // returns "0.0012300"

// if the number is < 1, precision is by the significant digits
num3.toPrecision(1);  // returns "0.001"

toFixed() 返回一个表示数字对象以定点表示法舍入后的字符串。此函数仅关注小数点后的数字。

const num1 = 123.4567;

// if no argument is passed, the fractions are removed
num1.toFixed();  // returns "123"

// specifying an argument means you the amount of numbers after the decimal point
num1.toFixed(1);  // returns "123.5"
num1.toFixed(3);  // returns "123.457"
num1.toFixed(5);  // returns "123.45670"
num1.toFixed(7);  // returns "123.4567000"

// trying to operator on number literals
2.34.toFixed(1);  // returns "2.3"
2.toFixed(1);     // returns SyntaxError
(2).toFixed(1);   // returns "2.0"
(2.34e+5).toFixed(1);  // returns "234000.0"

我之前提到过一个例外情况,即在负数字面量上使用这些函数将返回一个数字而不是字符串,这是由于运算符优先级的影响。以下是一些示例:

// Note: these are returning as Number
// toPrecision()
-123.45.toPrecision();  // returns -123.45
-123.45.toPrecision(2);  // returns -120
-123.45.toPrecision(4);  // returns -123.5
-2.34e+2.toPrecision(1);  // returns -200
-0.0456.toPrecision(1);  // returns -0.05
-0.0456.toPrecision(6);  // returns -0.0456

// toFixed()
-123.45.toFixed();  // returns -123.45
-123.45.toFixed(1);  // returns -123.5
-123.45.toFixed(4);  // returns -123.45
-0.0456.toFixed(1);  // returns -0
-0.0456.toFixed(6);  // -0.0456

有趣的事实:从-0.0456.toFixed(1)中可以看到有符号的零

参见:+0和-0是相同的吗?


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