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
视为相同,因此将它们向上舍入都给出了最小非正规值,向下舍入也是如此。
这是一个非常小众的问题,但我正在 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
视为相同,因此将它们向上舍入都给出了最小非正规值,向下舍入也是如此。