Skip to content

Commit 2622779

Browse files
authored
Remove is-close dependency (#1295)
This PR removes the external `is-close` dependency and replaces it with a custom implementation in the project's utility module. The change eliminates a third-party dependency while maintaining the same floating-point comparison functionality. Key changes: - Implemented a custom `isClose` function in `lib/utils.js` that matches the behavior of the `is-close` npm package - Updated all imports across test files and production code to use the new internal implementation - Removed the `is-close` dependency from `package.json` Fix: #1294
1 parent 898c8dc commit 2622779

File tree

6 files changed

+68
-19
lines changed

6 files changed

+68
-19
lines changed

lib/parameter.js

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
'use strict';
2121

22-
const IsClose = require('is-close');
22+
const { isClose } = require('./utils.js');
2323

2424
/**
2525
* The plus/minus tolerance for determining number equivalence.
@@ -623,8 +623,8 @@ class FloatingPointRange extends Range {
623623
const max = Math.max(this.fromValue, this.toValue);
624624

625625
if (
626-
IsClose.isClose(value, min, this.tolerance) ||
627-
IsClose.isClose(value, max, this.tolerance)
626+
isClose(value, min, this.tolerance) ||
627+
isClose(value, max, this.tolerance)
628628
) {
629629
return true;
630630
}
@@ -633,13 +633,7 @@ class FloatingPointRange extends Range {
633633
}
634634
if (this.step != 0.0) {
635635
const distanceInSteps = Math.round((value - min) / this.step);
636-
if (
637-
!IsClose.isClose(
638-
min + distanceInSteps * this.step,
639-
value,
640-
this.tolerance
641-
)
642-
) {
636+
if (!isClose(min + distanceInSteps * this.step, value, this.tolerance)) {
643637
return false;
644638
}
645639
}

lib/utils.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,62 @@ function detectUbuntuCodename() {
3232
}
3333
}
3434

35+
/**
36+
* Check if two numbers are equal within a given tolerance.
37+
*
38+
* This function compares two numbers using both relative and absolute tolerance,
39+
* matching the behavior of the 'is-close' npm package.
40+
*
41+
* The comparison uses the formula:
42+
* abs(a - b) <= max(rtol * max(abs(a), abs(b)), atol)
43+
*
44+
* Implementation checks:
45+
* 1. Absolute tolerance: abs(a - b) <= atol
46+
* 2. Relative tolerance: abs(a - b) / max(abs(a), abs(b)) <= rtol
47+
*
48+
* @param {number} a - The first number to compare
49+
* @param {number} b - The second number to compare
50+
* @param {number} [rtol=1e-9] - The relative tolerance parameter (default: 1e-9)
51+
* @param {number} [atol=0.0] - The absolute tolerance parameter (default: 0.0)
52+
* @returns {boolean} True if the numbers are close within the tolerance
53+
*
54+
* @example
55+
* isClose(1.0, 1.0) // true - exact equality
56+
* isClose(1.0, 1.1, 0.01) // false - relative diff: 0.1/1.1 ≈ 0.091 > 0.01
57+
* isClose(10, 10.00001, 1e-6) // true - relative diff: 0.00001/10 = 1e-6 <= 1e-6
58+
* isClose(0, 0.05, 0, 0.1) // true - absolute diff: 0.05 <= 0.1 (atol)
59+
*/
60+
function isClose(a, b, rtol = 1e-9, atol = 0.0) {
61+
// Handle exact equality
62+
if (a === b) {
63+
return true;
64+
}
65+
66+
// Handle non-finite numbers
67+
if (!Number.isFinite(a) || !Number.isFinite(b)) {
68+
return false;
69+
}
70+
71+
const absDiff = Math.abs(a - b);
72+
73+
// Check absolute tolerance first (optimization)
74+
if (atol >= absDiff) {
75+
return true;
76+
}
77+
78+
// Check relative tolerance
79+
const relativeScaler = Math.max(Math.abs(a), Math.abs(b));
80+
81+
// Handle division by zero when both values are zero or very close to zero
82+
if (relativeScaler === 0) {
83+
return true; // Both are zero, already handled by absolute tolerance
84+
}
85+
86+
const relativeDiff = absDiff / relativeScaler;
87+
88+
return rtol >= relativeDiff;
89+
}
3590
module.exports = {
3691
detectUbuntuCodename,
92+
isClose,
3793
};

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@
8080
"debug": "^4.4.0",
8181
"dot": "^1.1.3",
8282
"fs-extra": "^11.2.0",
83-
"is-close": "^1.3.3",
8483
"json-bigint": "^1.0.0",
8584
"node-addon-api": "^8.3.1",
8685
"walk": "^2.3.15"

test/test-node-oo.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
'use strict';
1616

17-
const IsClose = require('is-close');
17+
const { isClose } = require('../lib/utils.js');
1818
const assert = require('assert');
1919
const rclnodejs = require('../index.js');
2020
const assertUtils = require('./utils.js');
@@ -374,7 +374,7 @@ describe('rcl node methods testing', function () {
374374

375375
const seconds = Number(time.secondsAndNanoseconds.seconds);
376376
const dateSeconds = Date.now() / 1000;
377-
assert.ok(IsClose.isClose(seconds, dateSeconds, 1));
377+
assert.ok(isClose(seconds, dateSeconds, 1));
378378
});
379379

380380
it('node.getNodeNames', function () {

test/test-node.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
'use strict';
1616

17-
const IsClose = require('is-close');
17+
const { isClose } = require('../lib/utils.js');
1818
const assert = require('assert');
1919
const rclnodejs = require('../index.js');
2020
const assertUtils = require('./utils.js');
@@ -387,7 +387,7 @@ describe('rcl node methods testing', function () {
387387

388388
const seconds = Number(time.secondsAndNanoseconds.seconds);
389389
const dateSeconds = Date.now() / 1000;
390-
assert.ok(IsClose.isClose(seconds, dateSeconds, 1));
390+
assert.ok(isClose(seconds, dateSeconds, 1));
391391
});
392392

393393
it('node.getNodeNames', function () {

test/test-parameters.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
const assert = require('assert');
1818
const assertUtils = require('./utils.js');
1919
const assertThrowsError = assertUtils.assertThrowsError;
20-
const IsClose = require('is-close');
20+
const { isClose } = require('../lib/utils.js');
2121
const rclnodejs = require('../index.js');
2222
const loader = require('../lib/interface_loader.js');
2323

@@ -276,9 +276,9 @@ describe('rclnodejs parameters test suite', function () {
276276

277277
describe('range tests', function () {
278278
it('Math IsClose test', function () {
279-
assert.ok(IsClose.isClose(1.0, 1.0, DEFAULT_NUMERIC_RANGE_TOLERANCE));
280-
assert.ok(!IsClose.isClose(1.0, 1.1, DEFAULT_NUMERIC_RANGE_TOLERANCE));
281-
assert.ok(IsClose.isClose(1.0, 1.1, 0.11));
279+
assert.ok(isClose(1.0, 1.0, DEFAULT_NUMERIC_RANGE_TOLERANCE));
280+
assert.ok(!isClose(1.0, 1.1, DEFAULT_NUMERIC_RANGE_TOLERANCE));
281+
assert.ok(isClose(1.0, 1.1, 0.11));
282282
});
283283

284284
it('IntegerRange test', function () {

0 commit comments

Comments
 (0)