JavaScript 中的舍入模式

Rounding Modes in JavaScript

这是一个非常小众的问题,但我正在 JS 中实现区间运算,我想要正确的舍入。因此,我需要能够将两个数字相加并使其向无穷大、-无穷大、零等舍入。据我所知,JS 总是向零舍入并且这种行为不可改变,这与 C/C++ 与 fesetround.

我该如何解决这个问题?如果这意味着正确的舍入,我愿意对性能产生重大影响;可能该功能可以切换以平衡速度和正确性。也许一种方法是以某种方式使函数 roundUp 和 roundDown 将 up/down 舍入到下一个浮点值。

作为如何实施 roundUp/roundDown 的示例:


const floatStore = new Float64Array(1)

const intView = new Uint32Array(floatStore.buffer)

function roundUp(x) {
  if (x === Infinity)
    return Infinity
  if (x === -Infinity)
    return -Infinity
  if (isNaN(x))
    return NaN
    
  floatStore[0] = x

  let leastSignificantGroup = ++intView[0]

  if (leastSignificantGroup === 0)
    intView[1]++

  return floatStore[0]
}

(5.1).toPrecision(100) // -> 5.0999999999999996447...
roundUp(5.1).toPrecision(100) // -> 5.100000000000000532...

对于寻找快速获得连续浮点数的方法的任何人来说,这很有效:

const MAGIC_ROUND_C = 1.1113332476497816e-16 // just above machine epsilon / 2
const POSITIVE_NORMAL_MIN = 2.2250738585072014e-308
const POSITIVE_DENORMAL_MIN = Number.MIN_VALUE

function roundUp (x) {
  if (x >= -POSITIVE_NORMAL_MIN && x < POSITIVE_NORMAL_MIN) {
    // denormal numbers
    return x + POSITIVE_DENORMAL_MIN
  } else if (x === -Infinity) {
    // special case
    return -Number.MAX_VALUE
  }

  return x + Math.abs(x) * MAGIC_ROUND_C
}

function roundDown (x) {
  if (x > -POSITIVE_NORMAL_MIN && x <= POSITIVE_NORMAL_MIN) {
    return x - POSITIVE_DENORMAL_MIN
  } else if (x === Infinity) {
    return Number.MAX_VALUE
  }

  return x - Math.abs(x) * MAGIC_ROUND_C
}

唯一的奇怪之处在于它将 +0-0 视为相同,因此将它们向上舍入都给出了最小非正规值,向下舍入也是如此。