'Rounding Modes in JavaScript

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...


Solution 1:[1]

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.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1