Skip to content

Instantly share code, notes, and snippets.

@Yaffle
Last active February 2, 2017 22:49
Show Gist options
  • Save Yaffle/4654250 to your computer and use it in GitHub Desktop.
Save Yaffle/4654250 to your computer and use it in GitHub Desktop.
Math.nextAfter, Math.nextDown, Math.nextUp, Math.ulp in javascript
(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));
@jedwards1211
Copy link

I made this a nice npm package: https://github.com/jcoreio/ulp

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment