/*! \see http://floating-point-gui.de/errors/comparison/ \see https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ \see http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html */ import { M_LOG2 } from './constants'; export const EPSILON = .00001; export const DOUBLE_EPSILON = .000000000001; export const FACTOR = 100000; export const DOUBLE_FACTOR = 1000000000000; export function fuzzyIsNull(x: number, epsilon = EPSILON) { return Math.abs(x) <= epsilon; } //! \see https://github.com/toji/gl-matrix/blob/master/src/gl-matrix/common.js export function fuzzyEqual(x: number, y: number, epsilon = EPSILON) { return Math.abs(x - y) <= epsilon * Math.max(1, Math.abs(x), Math.abs(y)); } export function fuzzyEqual2(x: number, y: number, factor = FACTOR) { return Math.abs(x - y) * factor <= Math.min(Math.abs(x), Math.abs(y)); } export { fuzzyEqual as fuzzyCompare, fuzzyEqual2 as fuzzyCompare2 }; //! \see https://stackoverflow.com/questions/11832914/round-to-at-most-2-decimal-places-only-if-necessary export function fuzzyRound(x: number, factor = FACTOR) { return Math.round(x * factor) / factor; } // export function fuzzyRound2(x: number, epsilon = EPSILON) { // return Math.round(x / epsilon) * epsilon; // } //! \see https://stackoverflow.com/questions/7223359/are-0-and-0-the-same export function isMinusZero(x: number) { return x === 0 && 1 / x === -Infinity; } export function convertFloat64ToUint32(x: number, littleEndian?: boolean) { // slow, but precise (see https://jsperf.com/float64touint32) const view = new DataView(new ArrayBuffer(8)); view.setFloat64(0, x, littleEndian); return [view.getUint32(0, littleEndian), view.getUint32(4, littleEndian)] as [number, number]; } const M_POW_2_52 = Math.pow(2, 52); const M_POW_2_32 = Math.pow(2, 32); //! \see https://stackoverflow.com/questions/15935365/convert-float-to-bytes-in-javascript-without-float32array export function fuzzyConvertFloat64ToUint32(x: number, littleEndian?: boolean) { // fast, but fuzzy let hi = 0; let lo = 0; if (isNaN(x)) hi = 0x7ff80000; else if (x === Infinity) hi = 0x7ff00000; else if (x === -Infinity) hi = 0xfff00000; else if (isMinusZero(x)) hi = 0x80000000; else if (x !== 0) { if (x < 0) { hi = 0x80000000; x = -x; } let exp = Math.floor(Math.log(x) / M_LOG2); let sig = Math.floor((x / Math.pow(2, exp)) * M_POW_2_52); lo = sig & 0xffffffff; sig /= M_POW_2_32; exp += 0x3ff; if (exp >= 0x7ff) { exp = 0x7ff; sig = 0; } else if (exp < 0) exp = 0; hi |= exp << 20; hi |= sig & ~(-1 << 20); } return (littleEndian ? [lo >>> 0, hi >>> 0] : [hi >>> 0, lo >>> 0]) as [number, number]; }