Skip to content

Commit bcd2557

Browse files
committed
Added float tests for evaluateEquals
1 parent 8cec56d commit bcd2557

3 files changed

Lines changed: 98 additions & 21 deletions

File tree

src/Runtime.js

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,19 @@ const RuntimeError = require('./error/Runtime');
77
const Parser = require('./Parser');
88
const Lexer = require('./Lexer');
99

10-
// credit: http://stackoverflow.com/a/3886106/124861
11-
function isFloat(n) {
12-
return Number(n) === n && n % 1 !== 0;
10+
// credit: http://stackoverflow.com/a/9716515/124861
11+
function isNumeric(n) {
12+
return !isNaN(parseFloat(n)) && isFinite(n);
1313
}
1414

1515
function floatEqual(a, b, epsilon = Number.EPSILON) {
1616
if (a === b) {
1717
return true;
1818
}
1919

20-
if (isNaN(a) || isNaN(b) || !isFinite(a) || !isFinite(b)) {
21-
return false;
22-
}
23-
2420
const diff = Math.abs(a - b);
2521

26-
return diff < epsilon ? true : diff <= Math.max(Math.abs(a), Math.abs(b)) * epsilon;
22+
return diff <= epsilon || diff <= Math.min(Math.abs(a), Math.abs(b)) * epsilon;
2723
}
2824

2925
const {
@@ -49,7 +45,8 @@ const {
4945
} = require('./constants');
5046

5147
class Runner {
52-
constructor() {
48+
constructor(options = { epsilon: Number.EPSILON }) {
49+
this.epsilon = options.epsilon;
5350
this.astCache = {};
5451
this.result = [];
5552
}
@@ -181,8 +178,8 @@ class Runner {
181178
}
182179

183180
evaluateEquals(leftValue, rightValue, strict = false) {
184-
if (isFloat(leftValue) && isFloat(rightValue)) {
185-
return floatEqual(leftValue, rightValue);
181+
if (isNumeric(leftValue) && isNumeric(rightValue)) {
182+
return floatEqual(leftValue, rightValue, this.epsilon);
186183
}
187184
// eslint-disable-next-line eqeqeq
188185
return strict ? leftValue === rightValue : leftValue == rightValue;

test/.eslintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ extends: mitmaro/mocha
33
rules:
44
func-names: off
55
no-undefined: off
6+
max-nested-callbacks: off

test/Runtime/Runtime.evaluateEquals.spec.js

Lines changed: 89 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,113 @@
33
const expect = require('chai').expect;
44
const Runtime = require('../../src/Runtime');
55

6+
const runtime = new Runtime();
7+
68
describe('Runtime.evaluateEquals', function() {
79
it('should compare same loose equals on non-float', function() {
8-
const runtime = new Runtime();
9-
1010
expect(runtime.evaluateEquals(1, true)).to.be.true;
1111
});
1212

1313
it('should compare different loose equals on non-float', function() {
14-
const runtime = new Runtime();
15-
1614
expect(runtime.evaluateEquals(0, true)).to.be.false;
1715
});
1816

1917
it('should compare same strict equals on non-float', function() {
20-
const runtime = new Runtime();
21-
2218
expect(runtime.evaluateEquals('foo', 'foo', true)).to.be.true;
2319
});
2420

2521
it('should compare different types strict equals on non-float', function() {
26-
const runtime = new Runtime();
27-
2822
expect(runtime.evaluateEquals(true, 1, true)).to.be.false;
2923
});
3024

3125
it('should compare different values strict equals on non-float', function() {
32-
const runtime = new Runtime();
33-
3426
expect(runtime.evaluateEquals('foo', 'bar', true)).to.be.false;
3527
});
28+
29+
// cases adapted from: http://floating-point-gui.de/errors/NearlyEqualsTest.java
30+
describe('with numeric values', function() {
31+
[
32+
[1000000, 1000001],
33+
[1000001, 1000000],
34+
[-1000000, -1000001],
35+
[-1000001, -1000000],
36+
[1.0000001, 1.0000002],
37+
[1.0000002, 1.0000001],
38+
[-1.0000001, -1.0000002],
39+
[-1.0000002, -1.0000001],
40+
[0.000000001000001, 0.000000001000002],
41+
[0.000000001000002, 0.000000001000001],
42+
[-0.000000001000001, -0.000000001000002],
43+
[-0.000000001000002, -0.000000001000001],
44+
[0.0, 0.0],
45+
[0.0, -0.0],
46+
[-0.0, -0.0],
47+
[0, Math.pow(10, -40), 0.01],
48+
[Math.pow(10, -40), 0, 0.01],
49+
[0, -Math.pow(10, -40), 0.01],
50+
[-Math.pow(10, -40), 0, 0.01],
51+
[Number.MAX_VALUE, Number.MAX_VALUE],
52+
[Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],
53+
[Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
54+
[10 * Number.MIN_VALUE, 10 * -Number.MIN_VALUE],
55+
[Number.MIN_VALUE, Number.MIN_VALUE],
56+
[Number.MIN_VALUE, -Number.MIN_VALUE],
57+
[-Number.MIN_VALUE, Number.MIN_VALUE],
58+
[Number.MIN_VALUE, 0],
59+
[0, Number.MIN_VALUE],
60+
[-Number.MIN_VALUE, 0],
61+
[0, -Number.MIN_VALUE]
62+
].forEach((c) => {
63+
const epsilon = c[2] || 0.00001;
64+
65+
it(`should compare ${c[0]} to be equal to ${c[1]} with epsilon ${epsilon}`, function() {
66+
runtime.epsilon = epsilon;
67+
expect(runtime.evaluateEquals(...c)).to.be.true;
68+
});
69+
});
70+
[
71+
[10000, 10001],
72+
[10001, 10000],
73+
[-10000, -10001],
74+
[-10001, -10000],
75+
[1.0002, 1.0001],
76+
[1.0001, 1.0002],
77+
[-1.0001, -1.0002],
78+
[-1.0002, -1.0001],
79+
[Number.MAX_VALUE, -Number.MAX_VALUE],
80+
[-Number.MAX_VALUE, Number.MAX_VALUE],
81+
[Number.MAX_VALUE, Number.MAX_VALUE / 2],
82+
[Number.MAX_VALUE, -Number.MAX_VALUE / 2],
83+
[-Number.MAX_VALUE, Number.MAX_VALUE / 2],
84+
[Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY],
85+
[Number.POSITIVE_INFINITY, Number.MAX_VALUE],
86+
[Number.NEGATIVE_INFINITY, -Number.MAX_VALUE],
87+
[Number.NaN, Number.NaN],
88+
[Number.NaN, 0.0],
89+
[-0.0, Number.NaN],
90+
[Number.NaN, -0.0],
91+
[0.0, Number.NaN],
92+
[Number.NaN, Number.POSITIVE_INFINITY],
93+
[Number.POSITIVE_INFINITY, Number.NaN],
94+
[Number.NaN, Number.NEGATIVE_INFINITY],
95+
[Number.NEGATIVE_INFINITY, Number.NaN],
96+
[Number.NaN, Number.MAX_VALUE],
97+
[Number.MAX_VALUE, Number.NaN],
98+
[Number.NaN, -Number.MAX_VALUE],
99+
[-Number.MAX_VALUE, Number.NaN],
100+
[Number.NaN, Number.MIN_VALUE],
101+
[Number.MIN_VALUE, Number.NaN],
102+
[Number.NaN, -Number.MIN_VALUE],
103+
[-Number.MIN_VALUE, Number.NaN],
104+
[1.000000001, -1.0],
105+
[-1.0, 1.000000001],
106+
[-1.000000001, 1.0],
107+
[1.0, -1.000000001]
108+
].forEach((c) => {
109+
it(`should compare ${c[0]} to be not equal to ${c[1]} with epsilon 0.00001`, function() {
110+
runtime.epsilon = 0.00001;
111+
expect(runtime.evaluateEquals(...c)).to.be.false;
112+
});
113+
});
114+
});
36115
});

0 commit comments

Comments
 (0)