@lvlte/ulp 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ulp = exports.FLOAT64_MIN = void 0;
4
+ exports.eps = eps;
5
+ exports.exponent = exponent;
6
+ exports.nextFloat = nextFloat;
7
+ exports.prevFloat = prevFloat;
8
+ exports.FLOAT64_MIN = Math.pow(2, -1022);
9
+ function eps(x = 1) {
10
+ if (Number.isFinite(x)) {
11
+ x = Math.abs(x);
12
+ if (x <= exports.FLOAT64_MIN) {
13
+ return Number.MIN_VALUE;
14
+ }
15
+ return Math.pow(2, (_exponent(x) - 52));
16
+ }
17
+ return NaN;
18
+ }
19
+ exports.ulp = eps;
20
+ function exponent(x) {
21
+ if (Number.isFinite(x) && x !== 0) {
22
+ return _exponent(Math.abs(x));
23
+ }
24
+ return NaN;
25
+ }
26
+ function _exponent(x) {
27
+ const s = x.toString(2);
28
+ return x < 1 ? -(s.split('1', 1)[0].length - 1) : s.split('.', 1)[0].length - 1;
29
+ }
30
+ function nextFloat(x) {
31
+ switch (x) {
32
+ case -Infinity:
33
+ return -Number.MAX_VALUE;
34
+ case Infinity:
35
+ case Number.MAX_VALUE:
36
+ return Infinity;
37
+ case -Number.MIN_VALUE:
38
+ return -0;
39
+ default:
40
+ if (!Number.isFinite(x)) {
41
+ return NaN;
42
+ }
43
+ }
44
+ const e = eps(x);
45
+ const y = x + e / 2;
46
+ return y > x ? y : x + e;
47
+ }
48
+ function prevFloat(x) {
49
+ return typeof x === 'number' ? -nextFloat(-x) : NaN;
50
+ }
package/dist/index.d.ts CHANGED
@@ -5,7 +5,8 @@ export declare const FLOAT64_MIN: number;
5
5
  /**
6
6
  * Return the unit in the last place or unit of least precision (ulp) of x, that
7
7
  * is, the distance between two consecutive representable floating-point numbers
8
- * at x.
8
+ * at x. If x is a power of 2, the distance on either side of x is different, in
9
+ * which case the larger distance is returned.
9
10
  *
10
11
  * @param x The input number (default: 1)
11
12
  * @returns The ulp of x, or `NaN` if `x` is not a finite number.
@@ -19,7 +20,25 @@ export declare const ulp: typeof eps;
19
20
  * Exponent of a normalized floating-point number x.
20
21
  *
21
22
  * @param x The input number
22
- * @returns The largest integer `y` such that `2^y ≤ |x|`, or `NaN` if x is not
23
- * a finite number / if x is ±0.
23
+ * @returns The largest integer `y` such that `2^y ≤ |x|`. If `x` is not a
24
+ * finite number or equals ±0, returns `NaN`.
24
25
  */
25
26
  export declare function exponent(x: number): number;
27
+ /**
28
+ * Return the smallest representable floating-point number that comes after `x`
29
+ * on the float64 number line (towards +∞).
30
+ *
31
+ * @param x The input number
32
+ * @returns The smallest floating-point number `y` such that `y > x`. If `x` is
33
+ * `±Infinity` or `NaN`, returns `x`.
34
+ */
35
+ export declare function nextFloat(x: number): number;
36
+ /**
37
+ * Return the largest representable floating-point number that comes before `x`
38
+ * on the float64 number line (towards -∞).
39
+ *
40
+ * @param x The input number
41
+ * @returns The largest floating-point number `y` such that `y < x`. If `x` is
42
+ * `±Infinity` or `NaN`, returns `x`.
43
+ */
44
+ export declare function prevFloat(x: number): number;
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
- import { modf } from '@lvlte/modf';
2
1
  export const FLOAT64_MIN = Math.pow(2, -1022);
3
2
  export function eps(x = 1) {
4
3
  if (Number.isFinite(x)) {
@@ -18,9 +17,27 @@ export function exponent(x) {
18
17
  return NaN;
19
18
  }
20
19
  function _exponent(x) {
21
- const [ipart, fpart] = modf(x);
22
- if (ipart > 0) {
23
- return ipart.toString(2).split('.', 1)[0].length - 1;
20
+ const s = x.toString(2);
21
+ return x < 1 ? -(s.split('1', 1)[0].length - 1) : s.split('.', 1)[0].length - 1;
22
+ }
23
+ export function nextFloat(x) {
24
+ switch (x) {
25
+ case -Infinity:
26
+ return -Number.MAX_VALUE;
27
+ case Infinity:
28
+ case Number.MAX_VALUE:
29
+ return Infinity;
30
+ case -Number.MIN_VALUE:
31
+ return -0;
32
+ default:
33
+ if (!Number.isFinite(x)) {
34
+ return NaN;
35
+ }
24
36
  }
25
- return -(fpart.toString(2).split('1', 1)[0].length - 1);
37
+ const e = eps(x);
38
+ const y = x + e / 2;
39
+ return y > x ? y : x + e;
40
+ }
41
+ export function prevFloat(x) {
42
+ return typeof x === 'number' ? -nextFloat(-x) : NaN;
26
43
  }
package/package.json CHANGED
@@ -1,25 +1,21 @@
1
1
  {
2
2
  "name": "@lvlte/ulp",
3
- "version": "1.1.0",
4
- "description": "Compute the unit in last place of a given IEEE-754 64-bit number",
3
+ "version": "1.2.0",
4
+ "description": "Compute the ULP of a given number / Get the closest representable number that comes before/after it on the float64 number line",
5
5
  "license": "MIT",
6
6
  "author": "Eric Lavault <lvlte.code@gmail.com>",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "git+https://github.com/lvlte/ulp"
9
+ "url": "git+https://github.com/lvlte/ulp.git"
10
10
  },
11
11
  "type": "module",
12
- "main": "./dist/cjs/index.js",
12
+ "main": "./dist/index.cjs",
13
+ "types": "./dist/index.d.ts",
13
14
  "exports": {
14
15
  ".": {
15
- "import": {
16
- "types": "./dist/index.d.ts",
17
- "default": "./dist/index.js"
18
- },
19
- "require": {
20
- "types": "./dist/cjs/index.d.ts",
21
- "default": "./dist/cjs/index.js"
22
- }
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.js",
18
+ "require": "./dist/index.cjs"
23
19
  }
24
20
  },
25
21
  "engines": {
@@ -32,35 +28,33 @@
32
28
  "test": "jest",
33
29
  "prebuild": "rimraf dist && make-dir dist",
34
30
  "build": "tsc && tsc -p tsconfig-cjs.json",
35
- "postbuild": "(echo { && echo \"type\": \"commonjs\" && echo }) > ./dist/cjs/package.json",
36
- "prepublishOnly": "npm test && npm run build && tsc --emitDeclarationOnly true --removeComments false && tsc -p tsconfig-cjs.json --emitDeclarationOnly true --removeComments false"
31
+ "postbuild": "mv dist/cjs/index.js dist/index.cjs && rimraf dist/cjs",
32
+ "prepublishOnly": "npm test && npm run build && tsc --emitDeclarationOnly true --removeComments false"
37
33
  },
38
34
  "keywords": [
39
35
  "number",
40
36
  "epsilon",
41
- "function",
42
37
  "precision",
43
38
  "eps",
44
39
  "ulp",
40
+ "nextFloat",
41
+ "prevFloat",
42
+ "next",
43
+ "previous",
45
44
  "float",
46
45
  "float64",
47
46
  "IEEE-754",
48
47
  "rounding",
49
48
  "tolerance",
50
- "compare",
51
- "equal",
52
49
  "ULP"
53
50
  ],
54
51
  "devDependencies": {
55
- "@lvlte/tsconfig": "^1.0.1",
52
+ "@lvlte/tsconfig": "^1.1.0",
56
53
  "@types/jest": "^29.5.12",
57
54
  "jest": "^29.7.0",
58
55
  "make-dir-cli": "^4.0.0",
59
56
  "rimraf": "^6.0.1",
60
57
  "ts-jest": "29.2.5",
61
- "typescript": "^5.9.2"
62
- },
63
- "dependencies": {
64
- "@lvlte/modf": "^1.1.0"
58
+ "typescript": "^5.9.3"
65
59
  }
66
60
  }
package/readme.md CHANGED
@@ -1,7 +1,10 @@
1
1
  # ulp (epsilon function)
2
2
 
3
3
  > Compute the [unit of least precision](https://en.wikipedia.org/wiki/Unit_in_the_last_place)
4
- of a given IEEE-754 64-bit number.
4
+ of a given IEEE-754 64-bit number : `eps(x)` (alias `ulp(x)`).<br>
5
+ > Get the closest representable number that comes before/after it on the float64
6
+ number line : `nextFloat(x)`, `prevFloat(x)`.
7
+
5
8
 
6
9
  ## Install
7
10
 
@@ -13,28 +16,43 @@ npm install @lvlte/ulp
13
16
 
14
17
  ```js
15
18
  // ESM
16
- import { eps } from '@lvlte/ulp';
19
+ import { eps, nextFloat, prevFloat } from '@lvlte/ulp';
17
20
  ```
18
21
  ```js
19
22
  // CJS
20
- const { eps } = require('@lvlte/ulp');
23
+ const { eps, nextFloat, prevFloat } = require('@lvlte/ulp');
24
+ ```
25
+ ```js
26
+ console.log( eps() ); // 2.220446049250313e-16
27
+ console.log( eps() === eps(1) ); // true
28
+ console.log( eps() === Number.EPSILON ); // true
29
+
30
+ console.log( eps(Number.MAX_SAFE_INTEGER) ); // 1
31
+ console.log( eps(Number.MAX_SAFE_INTEGER + 1) ); // 2
32
+
33
+ console.log( eps(0) ); // 5e-324
34
+ console.log( eps(0) === Number.MIN_VALUE ); // true
35
+
36
+ console.log( eps(Infinity) ); // NaN
21
37
  ```
22
38
  ```js
23
- console.log(eps()); // 2.220446049250313e-16
24
- console.log(eps() === eps(1)); // true
25
- console.log(eps() === Number.EPSILON); // true
39
+ console.log( nextFloat(0) ); // 5e-324
40
+ console.log( nextFloat(0) === Number.MIN_VALUE ); // true
26
41
 
27
- console.log(eps(Number.MAX_SAFE_INTEGER)); // 1
28
- console.log(eps(Number.MAX_SAFE_INTEGER + 1)); // 2
42
+ console.log( nextFloat(1) ); // 1.0000000000000002
43
+ console.log( nextFloat(1) === 1 + eps(1) ); // true
29
44
 
30
- console.log(eps(0)); // 5e-324
31
- console.log(eps(0) === Number.MIN_VALUE); // true
45
+ console.log( nextFloat(Number.MAX_SAFE_INTEGER) ); // 9007199254740992
46
+ console.log( nextFloat(9007199254740992) ); // 9007199254740994
47
+ console.log( nextFloat(Number.MAX_VALUE) ); // Infinity
32
48
 
33
- console.log(eps(Infinity)); // NaN
49
+ console.log( prevFloat(0) ); // -5e-324
50
+ console.log( prevFloat(1) ); // 0.9999999999999999
51
+ console.log( prevFloat(9007199254740992) ); // 9007199254740991
52
+ console.log( prevFloat(Infinity) ); // 1.7976931348623157e+308
34
53
  ```
35
54
 
36
55
  ## Use case
37
56
 
38
- - Check whether two floating point numbers can be considered equal.
39
- - Approximate a floating point number x as a rational number.
40
- - Get the next/previous float on the float64 number line.
57
+ - Use the proper tolerance to check whether two floating-point numbers should be considered equal.
58
+ - Use the proper tolerance to approximate a floating-point number as a rational number.
@@ -1,25 +0,0 @@
1
- /**
2
- * The smallest positive normal number representable by IEEE-754 float64.
3
- */
4
- export declare const FLOAT64_MIN: number;
5
- /**
6
- * Return the unit in the last place or unit of least precision (ulp) of x, that
7
- * is, the distance between two consecutive representable floating-point numbers
8
- * at x.
9
- *
10
- * @param x The input number (default: 1)
11
- * @returns The ulp of x, or `NaN` if `x` is not a finite number.
12
- */
13
- export declare function eps(x?: number): number;
14
- /**
15
- * @borrows eps as ulp
16
- */
17
- export declare const ulp: typeof eps;
18
- /**
19
- * Exponent of a normalized floating-point number x.
20
- *
21
- * @param x The input number
22
- * @returns The largest integer `y` such that `2^y ≤ |x|`, or `NaN` if x is not
23
- * a finite number / if x is ±0.
24
- */
25
- export declare function exponent(x: number): number;
package/dist/cjs/index.js DELETED
@@ -1,31 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ulp = exports.FLOAT64_MIN = void 0;
4
- exports.eps = eps;
5
- exports.exponent = exponent;
6
- const modf_1 = require("@lvlte/modf");
7
- exports.FLOAT64_MIN = Math.pow(2, -1022);
8
- function eps(x = 1) {
9
- if (Number.isFinite(x)) {
10
- x = Math.abs(x);
11
- if (x <= exports.FLOAT64_MIN) {
12
- return Number.MIN_VALUE;
13
- }
14
- return Math.pow(2, (_exponent(x) - 52));
15
- }
16
- return NaN;
17
- }
18
- exports.ulp = eps;
19
- function exponent(x) {
20
- if (Number.isFinite(x) && x !== 0) {
21
- return _exponent(Math.abs(x));
22
- }
23
- return NaN;
24
- }
25
- function _exponent(x) {
26
- const [ipart, fpart] = (0, modf_1.modf)(x);
27
- if (ipart > 0) {
28
- return ipart.toString(2).split('.', 1)[0].length - 1;
29
- }
30
- return -(fpart.toString(2).split('1', 1)[0].length - 1);
31
- }
@@ -1,3 +0,0 @@
1
- {
2
- "type": "commonjs"
3
- }