Last active
February 2, 2017 22:49
-
-
Save Yaffle/4654250 to your computer and use it in GitHub Desktop.
Math.nextAfter, Math.nextDown, Math.nextUp, Math.ulp in javascript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function (global) { | |
"use strict"; | |
// Math.nextUp | |
// Note: | |
// Math.nextDown = function (x) { return -Math.nextUp(-x); }; | |
// Math.nextAfter = function (x, y) { return y < x ? -Math.nextUp(-x) : (y > x ? Math.nextUp(x) : (x !== x ? x : y)); }; | |
// Math.ulp = function (x) { return x < 0 ? Math.nextUp(x) - x : x - (-Math.nextUp(-x)); }; | |
var EPSILON = Math.pow(2, -52); | |
var MAX_VALUE = (2 - EPSILON) * Math.pow(2, 1023); | |
var MIN_VALUE = Math.pow(2, -1022); | |
Math.nextUp = function (x) { | |
if (x !== x) { | |
return x; | |
} | |
if (x === -1 / 0) { | |
return -MAX_VALUE; | |
} | |
if (x === +1 / 0) { | |
return +1 / 0; | |
} | |
if (x === +MAX_VALUE) { | |
return +1 / 0; | |
} | |
var y = x * (x < 0 ? 1 - EPSILON / 2 : 1 + EPSILON); | |
if (y === x) { | |
y = MIN_VALUE * EPSILON > 0 ? x + MIN_VALUE * EPSILON : x + MIN_VALUE; | |
} | |
if (y === +1 / 0) { | |
y = +MAX_VALUE; | |
} | |
var b = x + (y - x) / 2; | |
if (x < b && b < y) { | |
y = b; | |
} | |
var c = (y + x) / 2; | |
if (x < c && c < y) { | |
y = c; | |
} | |
return y === 0 ? -0 : y; | |
}; | |
function assertEquals(actual, expected) { | |
var ok = (actual === expected && (actual !== 0 || 1 / actual === 1 / expected)) || (actual !== actual && expected !== expected); | |
console.assert(ok, {actual: actual, expected: expected}); | |
} | |
// denormalized values may not be supported | |
// MIN_VALUE is 2.2250738585072014e-308, when should be 5e-324 on Opera Mobile | |
var special = [ | |
0 / 0, | |
1 / 0, | |
MAX_VALUE, | |
1, | |
MIN_VALUE, | |
MIN_VALUE * EPSILON, | |
0 | |
]; | |
assertEquals(Math.nextUp(+0 / 0), 0 / 0); | |
assertEquals(Math.nextUp(-0 / 0), 0 / 0); | |
assertEquals(Math.nextUp(+1 / 0), +1 / 0); | |
assertEquals(Math.nextUp(-1 / 0), -MAX_VALUE); | |
assertEquals(Math.nextUp(+MAX_VALUE), +1 / 0); | |
assertEquals(Math.nextUp(-MAX_VALUE), -1.7976931348623155e+308); | |
assertEquals(Math.nextUp(+1), +1 + EPSILON); | |
assertEquals(Math.nextUp(-1), -1 + EPSILON / 2); | |
assertEquals(Math.nextUp(+MIN_VALUE), +2.225073858507202e-308); | |
assertEquals(Math.nextUp(-MIN_VALUE), -2.225073858507201e-308); | |
assertEquals(Math.nextUp(+0), MIN_VALUE * EPSILON > 0 ? MIN_VALUE * EPSILON : MIN_VALUE); | |
assertEquals(Math.nextUp(-0), MIN_VALUE * EPSILON > 0 ? MIN_VALUE * EPSILON : MIN_VALUE); | |
var i = -1; | |
while (++i < special.length) { | |
var value = special[i]; | |
if (value !== +1 / 0) { | |
var j = -1; | |
while (++j < 2) { | |
assertEquals(0 + Math.nextUp(-Math.nextUp(-value)), 0 + value); | |
value = -value; | |
} | |
} | |
} | |
if (global.Float64Array && global.Uint16Array) { | |
var float64Array = new global.Float64Array(1); | |
var uint16Array = new global.Uint16Array(float64Array.buffer); | |
Math.nextUp2 = function (x) { | |
if (x !== x) { | |
return 0 / 0; | |
} | |
if (x === +1 / 0) { | |
return x; | |
} | |
if (x === 0) { | |
return MIN_VALUE * EPSILON > 0 ? MIN_VALUE * EPSILON : MIN_VALUE; | |
} | |
float64Array[0] = x; | |
var n = x < 0 ? -1 : 1; | |
var i = -1; | |
while (++i < 4) { | |
n += uint16Array[i]; | |
uint16Array[i] = n & 0xffff; | |
n >>= 16; | |
} | |
return float64Array[0]; | |
}; | |
var i = -1; | |
while (++i < special.length) { | |
var value = special[i]; | |
var j = -1; | |
while (++j < 2) { | |
assertEquals(Math.nextUp(value), Math.nextUp2(value)); | |
value = -value; | |
} | |
} | |
var crypto = global.crypto || global.msCrypto; | |
if (crypto && crypto.getRandomValues) { | |
var c = 0; | |
while (++c < 1024 * 16) { | |
crypto.getRandomValues(uint16Array); | |
var v = float64Array[0]; | |
assertEquals(Math.nextUp(v), Math.nextUp2(v)); | |
} | |
} | |
console.log("ok!"); | |
} | |
}(this)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I made this a nice npm package: https://github.com/jcoreio/ulp