如何向上、向下或四舍五入数字

控制 JavaScript 在使用不同舍入模式格式化时如何舍入小数

介绍

当您格式化数字以供显示时,通常需要对小数值进行四舍五入。例如,价格 $2.567 需要变为 $2.57,测量值 3.891 米可能显示为 4 米。数字的四舍五入方式会影响精度、用户期望和业务逻辑。

不同的情况需要不同的四舍五入策略。有时您需要向上取整以确保对产品收费足够。有时您需要向下取整以保持在预算范围内。大多数情况下,您会四舍五入到最接近的值以保持精度。

JavaScript 通过 Intl.NumberFormat API 提供了九种不同的四舍五入模式。这些模式控制当数字介于两个可能值之间时如何进行四舍五入。本课程将解释三种最常见的四舍五入模式,并展示何时使用其他模式。

JavaScript 默认如何四舍五入数字

当您在格式化数字时未指定四舍五入模式,JavaScript 使用一种称为 halfExpand 的策略。此模式将值四舍五入到最接近的可能值,当数字正好位于两个值的中间时,它会向零的远离方向四舍五入。

const formatter = new Intl.NumberFormat("en-US", {
  maximumFractionDigits: 0
});

console.log(formatter.format(2.4));
// 输出: "2"

console.log(formatter.format(2.5));
// 输出: "3"

console.log(formatter.format(2.6));
// 输出: "3"

值 2.4 更接近 2 而不是 3,因此它向下取整为 2。值 2.6 更接近 3 而不是 2,因此它向上取整为 3。值 2.5 正好位于 2 和 3 的中间,因此 halfExpand 模式将其向零的远离方向四舍五入为 3。

这种默认行为符合大多数人在学校学习的内容以及日常计算中的期望。它在许多计算中均匀分布了四舍五入误差,使其适合于通用的数字格式化。

理解“远离零”的含义

短语“远离零”描述了当数字正好位于两个可能值之间时的舍入方向。对于正数,远离零的舍入意味着向上舍入。对于负数,远离零的舍入意味着向下舍入到更大的绝对值。

const formatter = new Intl.NumberFormat("en-US", {
  maximumFractionDigits: 0
});

console.log(formatter.format(2.5));
// 输出: "3"

console.log(formatter.format(-2.5));
// 输出: "-3"

2.5 和 -2.5 都是远离零舍入。对于 2.5,远离零意味着向正无穷方向舍入,结果是 3。对于 -2.5,远离零意味着向负无穷方向舍入,结果是 -3。在这两种情况下,绝对值都增加了。

使用 ceil 向上舍入

ceil 舍入模式总是向正无穷方向舍入。对于正数,这意味着向上舍入。对于负数,这意味着向零方向舍入。

const formatter = new Intl.NumberFormat("en-US", {
  maximumFractionDigits: 0,
  roundingMode: "ceil"
});

console.log(formatter.format(2.1));
// 输出: "3"

console.log(formatter.format(2.9));
// 输出: "3"

console.log(formatter.format(-2.1));
// 输出: "-2"

console.log(formatter.format(-2.9));
// 输出: "-2"

这种模式在需要确保数字不小于必要值时非常有用。例如,如果计算出一个箱子可以容纳 2.3 件物品,则需要 3 个箱子。如果计算出一项任务需要 1.1 天,则需要计划 2 天。

const boxFormatter = new Intl.NumberFormat("en-US", {
  maximumFractionDigits: 0,
  roundingMode: "ceil"
});

const itemsPerBox = 5;
const totalItems = 12;
const boxesNeeded = totalItems / itemsPerBox;

console.log(boxFormatter.format(boxesNeeded));
// 输出: "3"

计算结果是 2.4 个箱子,但无法订购部分箱子。向上舍入确保了足够的容量。

使用 floor 向下取整

floor 取整模式始终向负无穷方向取整。对于正数,这意味着向下取整。对于负数,这意味着远离零取整。

const formatter = new Intl.NumberFormat("en-US", {
  maximumFractionDigits: 0,
  roundingMode: "floor"
});

console.log(formatter.format(2.1));
// 输出: "2"

console.log(formatter.format(2.9));
// 输出: "2"

console.log(formatter.format(-2.1));
// 输出: "-3"

console.log(formatter.format(-2.9));
// 输出: "-3"

这种模式在需要保守估计或希望保持在限制范围内时非常有用。例如,如果您的预算是 $100.87,您可能会将其显示为 $100,以避免意外超支。

const budgetFormatter = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  maximumFractionDigits: 0,
  roundingMode: "floor"
});

const availableBudget = 100.87;

console.log(budgetFormatter.format(availableBudget));
// 输出: "$100"

向下取整确保显示的金额始终在实际预算范围内。

使用 halfExpand 四舍五入到最近值

虽然 halfExpand 是默认模式,但您可以显式指定它以使代码意图更加清晰。这种模式会四舍五入到最近的值,并在中间值的情况下远离零取整。

const formatter = new Intl.NumberFormat("en-US", {
  maximumFractionDigits: 1,
  roundingMode: "halfExpand"
});

console.log(formatter.format(2.14));
// 输出: "2.1"

console.log(formatter.format(2.15));
// 输出: "2.2"

console.log(formatter.format(2.16));
// 输出: "2.2"

值 2.14 更接近 2.1,因此取整为 2.1。值 2.16 更接近 2.2,因此取整为 2.2。值 2.15 恰好在中间,因此远离零取整为 2.2。

这种模式适用于大多数数字格式化任务,因为它可以最小化多次计算中的总取整误差。每个中间值在零的正负两侧有相等的机会,因此取整方向随着时间的推移会平衡。

将舍入模式与小数位数结合使用

舍入模式与小数位数设置一起工作以控制最终输出。maximumFractionDigits 选项决定显示的小数位数,而 roundingMode 决定如何处理介于可表示数字之间的值。

const ceilFormatter = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
  roundingMode: "ceil"
});

console.log(ceilFormatter.format(10.001));
// 输出: "$10.01"

console.log(ceilFormatter.format(10.999));
// 输出: "$11.00"

在两位小数的情况下,10.001需要舍入到10.00或10.01。ceil 模式向上舍入,生成10.01。值10.999向上舍入为11.00。

使用 trunc 向零舍入

trunc 舍入模式向零舍入,这意味着它会移除数字的小数部分。对于正数,这会向下舍入。对于负数,这会向上舍入。

const formatter = new Intl.NumberFormat("en-US", {
  maximumFractionDigits: 0,
  roundingMode: "trunc"
});

console.log(formatter.format(2.1));
// 输出: "2"

console.log(formatter.format(2.9));
// 输出: "2"

console.log(formatter.format(-2.1));
// 输出: "-2"

console.log(formatter.format(-2.9));
// 输出: "-2"

此模式有效地截断了小数部分。当您只想显示数字的整数部分而不考虑小数值的舍入时,此模式非常有用。

使用 expand 远离零舍入

expand 舍入模式远离零舍入。对于正数,这会向上舍入。对于负数,这会向下舍入到更大的绝对值。

const formatter = new Intl.NumberFormat("en-US", {
  maximumFractionDigits: 0,
  roundingMode: "expand"
});

console.log(formatter.format(2.1));
// 输出: "3"

console.log(formatter.format(2.9));
// 输出: "3"

console.log(formatter.format(-2.1));
// 输出: "-3"

console.log(formatter.format(-2.9));
// 输出: "-3"

此模式确保舍入始终增加数字的绝对值。在金融环境中,当您希望在有利于准确性而非低估的方向上保守时,此模式可能非常有用。

理解四舍五入模式

half 开头的五种模式都将值四舍五入到最接近的值,但它们在处理正好处于中间的情况时有所不同。这些模式为您提供了对值正好位于两个可表示数字之间时的平局处理行为的精细控制。

halfCeil 模式将中间值向正无穷大方向舍入。

const formatter = new Intl.NumberFormat("en-US", {
  maximumFractionDigits: 0,
  roundingMode: "halfCeil"
});

console.log(formatter.format(2.5));
// 输出: "3"

console.log(formatter.format(-2.5));
// 输出: "-2"

halfFloor 模式将中间值向负无穷大方向舍入。

const formatter = new Intl.NumberFormat("en-US", {
  maximumFractionDigits: 0,
  roundingMode: "halfFloor"
});

console.log(formatter.format(2.5));
// 输出: "2"

console.log(formatter.format(-2.5));
// 输出: "-3"

halfTrunc 模式将中间值向零方向舍入。

const formatter = new Intl.NumberFormat("en-US", {
  maximumFractionDigits: 0,
  roundingMode: "halfTrunc"
});

console.log(formatter.format(2.5));
// 输出: "2"

console.log(formatter.format(-2.5));
// 输出: "-2"

halfEven 模式将中间值舍入到最接近的偶数。这种模式有时被称为银行家舍入,因为它减少了财务计算中的偏差。

const formatter = new Intl.NumberFormat("en-US", {
  maximumFractionDigits: 0,
  roundingMode: "halfEven"
});

console.log(formatter.format(2.5));
// 输出: "2"

console.log(formatter.format(3.5));
// 输出: "4"

console.log(formatter.format(4.5));
// 输出: "4"

值 2.5 舍入为 2,因为 2 是偶数。值 3.5 舍入为 4,因为 4 是偶数。值 4.5 也舍入为 4,因为 4 是偶数。

为您的应用选择舍入模式

您选择的舍入模式取决于您的业务需求和您显示的数据的性质。不同的上下文需要不同的策略。

当您需要确保充足时,请使用 ceil。容量规划、库存计数和时间估算通常需要向上舍入以保证足够的资源。

当您需要保持在限制范围内时,请使用 floor。预算显示、配额跟踪和折扣计算通常需要向下舍入以避免超出可用资源。

当准确性重要但不需要极高精度时,请使用 halfExpand 进行通用显示。这是默认模式,适用于大多数数字格式化任务。

当您需要最小化累计舍入偏差时,请使用 halfEven 进行财务计算。这种模式确保在多次计算中,舍入误差不会始终偏向一个方向。

当您只想显示数字的整数部分而不对小数部分应用舍入逻辑时,请使用 trunc

使用舍入模式进行货币格式化

舍入模式可以自然地与货币格式化结合使用。不同的企业对如何舍入货币值有不同的规则,roundingMode 选项可以让您实现这些规则。

const retailPrice = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
  roundingMode: "ceil"
});

const wholesalePrice = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
  roundingMode: "floor"
});

const calculatedPrice = 19.874;

console.log(retailPrice.format(calculatedPrice));
// 输出: "$19.88"

console.log(wholesalePrice.format(calculatedPrice));
// 输出: "$19.87"

零售商可能会将价格向上舍入以确保盈利,而批发商可能会将价格向下舍入以保持竞争力。同一个计算价格会根据舍入模式中编码的业务规则显示不同的值。