This is a pretty niche question, but I'm implementing interval arithmetic in JS and I'd like to have correct rounding. Therefore, I need to be able to add two Numbers and have it round towards infinity, -infinity, zero, etc. As far as I can tell JS always rounds towards zero and this behavior isn't changeable, unlike in C/C++ with fesetround.
How can I get around this? I'm willing to have significant performance impacts if it means correct rounding; probably the feature will be toggleable to balance speed and correctness. Perhaps one way to do this would be to somehow make functions roundUp and roundDown which round up/down to the next float value.
As an example of how roundUp/roundDown could be implemented:
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...
For anyone looking for a fast way to get consecutive floats, this works:
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
}
The only oddity is that it treats +0
and -0
as the same, so rounding them up both gives the minimum denormal, and same for rounding down.