Skip to content

Commit 9d4f69a

Browse files
committed
rounding using both precision and decimals
1 parent 4bd3b81 commit 9d4f69a

File tree

6 files changed

+137
-5
lines changed

6 files changed

+137
-5
lines changed

build/math-expressions.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/math-expressions_umd.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/expression/round.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,60 @@ const round_numbers_to_decimals_sub = function(tree, ndecimals=0) {
8989
return [operator, ...operands.map(x => round_numbers_to_decimals_sub(x,ndecimals))]
9090
}
9191

92+
93+
94+
export function round_numbers_to_precision_plus_decimals(expr_or_tree, digits=4, ndecimals=2) {
95+
// round any decimals to specified number of significant digits
96+
// but increase number of digits to ensure number of decimals
97+
98+
var tree = get_tree(expr_or_tree);
99+
100+
if(digits < 1) {
101+
throw Error("For round_numbers_to_precision_plus_decimals, digits must be positive");
102+
}
103+
104+
if(!Number.isFinite(digits)) {
105+
throw Error("For round_numbers_to_precision_plus_decimals, digits must be a number");
106+
}
107+
108+
if(!Number.isFinite(ndecimals)) {
109+
throw Error("For round_numbers_to_precision_plus_decimals, ndecimals must be a number");
110+
}
111+
112+
if(digits > 15) {
113+
return tree;
114+
}
115+
116+
digits = Math.round(digits);
117+
118+
ndecimals = Math.round(ndecimals);
119+
120+
// no need to go much beyond limits of double precision
121+
ndecimals = Math.max(-330, Math.min(330, ndecimals));
122+
123+
return round_numbers_to_precision_plus_decimals_sub(tree,digits, ndecimals);
124+
}
125+
126+
const round_numbers_to_precision_plus_decimals_sub = function(tree, digits=4, ndecimals=2) {
127+
128+
if(typeof tree === "number") {
129+
130+
if(Number.isFinite(tree) && tree !== 0) {
131+
const scaleFactor = math.floor(math.log10(math.abs(tree)));
132+
const n = Math.max(digits - scaleFactor - 1, ndecimals);
133+
if(n < 0) {
134+
// mathjs toFixed truncates zeros when n is negative
135+
// so add back on when creating float
136+
return parseFloat(toFixed(tree, n)+'0'.repeat(math.abs(n)));
137+
} else {
138+
return parseFloat(toFixed(tree, n))
139+
}
140+
}
141+
}
142+
if(!Array.isArray(tree)) {
143+
return tree;
144+
}
145+
let operator = tree[0];
146+
let operands = tree.slice(1);
147+
return [operator, ...operands.map(x => round_numbers_to_precision_plus_decimals_sub(x,digits,ndecimals))]
148+
}

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "math-expressions",
33
"description": "Perform basic equality testing and symbolic computations on mathematical expressions involving transcendental functions",
4-
"version": "2.0.0-alpha60",
4+
"version": "2.0.0-alpha61",
55
"author": {
66
"name": "Jim Fowler",
77
"email": "[email protected]",

spec/quick_rounding.spec.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,79 @@ describe("round to decimals", function () {
147147
});
148148

149149

150+
describe("round to significant digits plus decimals", function () {
151+
152+
it("single number", function () {
153+
154+
let expr = me.fromText('1.234567890123456789');
155+
expect(expr.round_numbers_to_precision_plus_decimals(100,6).tree).toEqual(1.2345678901234567)
156+
expect(expr.round_numbers_to_precision_plus_decimals(14,6).tree).toEqual(1.2345678901235)
157+
expect(expr.round_numbers_to_precision_plus_decimals(10,6).tree).toEqual(1.23456789)
158+
expect(expr.round_numbers_to_precision_plus_decimals(4,6).tree).toEqual(1.234568)
159+
160+
expr = me.fromText('12345678901234567890000000000');
161+
expect(expr.round_numbers_to_precision_plus_decimals(100,-17).tree).toEqual(12345678901234568000000000000)
162+
expect(expr.round_numbers_to_precision_plus_decimals(14,-17).tree).toEqual(12345678901235000000000000000)
163+
expect(expr.round_numbers_to_precision_plus_decimals(10,-17).tree).toEqual(12345678901200000000000000000)
164+
expect(expr.round_numbers_to_precision_plus_decimals(4,-17).tree).toEqual(12345678901200000000000000000)
165+
166+
expr = me.fromText('0.000000000000000000001234567890123456789');
167+
expect(expr.round_numbers_to_precision_plus_decimals(100,27).tree).toEqual(0.0000000000000000000012345678901234568)
168+
expect(expr.round_numbers_to_precision_plus_decimals(14,27).tree).toEqual(0.0000000000000000000012345678901235)
169+
expect(expr.round_numbers_to_precision_plus_decimals(10,27).tree).toEqual(0.00000000000000000000123456789)
170+
expect(expr.round_numbers_to_precision_plus_decimals(4,27).tree).toEqual(0.000000000000000000001234568)
171+
172+
expect(me.fromAst(0).round_numbers_to_precision_plus_decimals(10,2).tree).toEqual(0)
173+
expect(me.fromAst(Infinity).round_numbers_to_precision_plus_decimals(10,2).tree).toEqual(Infinity)
174+
expect(me.fromAst(-Infinity).round_numbers_to_precision_plus_decimals(10,2).tree).toEqual(-Infinity)
175+
176+
});
177+
178+
it("expression", function () {
179+
180+
let expr = me.fromText('exp(1.234567890123456789x+9.876543210987654321)/(5+8520203156.435345956432x)');
181+
expect(expr.round_numbers_to_precision_plus_decimals(100,5).equals(
182+
me.fromText('exp(1.2345678901234567 x + 9.876543210987654)/(5 + 8520203156.435346 x)'))).toBeTruthy();
183+
expect(expr.round_numbers_to_precision_plus_decimals(14,5).equals(
184+
me.fromText('exp(1.2345678901235 x + 9.8765432109877)/(5 + 8520203156.43535 x)'))).toBeTruthy();
185+
expect(expr.round_numbers_to_precision_plus_decimals(10,5).equals(
186+
me.fromText('exp(1.23456789 x + 9.876543211)/(5 + 8520203156.43535 x)'))).toBeTruthy();
187+
expect(expr.round_numbers_to_precision_plus_decimals(4,5).equals(
188+
me.fromText('exp(1.23457 x + 9.87654)/(5 + 8520203156.43535 x)'))).toBeTruthy();
189+
190+
});
191+
192+
it("don't round fractions", function () {
193+
194+
let expr = me.fromText('3/7x + 381439619649.253 y');
195+
expect(expr.round_numbers_to_precision_plus_decimals(100,-2).equals(
196+
me.fromText('3/7x + 381439619649.253 y'))).toBeTruthy();
197+
expect(expr.round_numbers_to_precision_plus_decimals(14,-2).equals(
198+
me.fromText('3/7x + 381439619649.25 y'))).toBeTruthy();
199+
expect(expr.round_numbers_to_precision_plus_decimals(10,-2).equals(
200+
me.fromText('3/7x + 381439619600 y'))).toBeTruthy();
201+
expect(expr.round_numbers_to_precision_plus_decimals(4,-2).equals(
202+
me.fromText('3/7x + 381439619600 y'))).toBeTruthy();
203+
204+
});
205+
206+
207+
it("don't round pi or e", function () {
208+
209+
let expr = me.fromText('3/7e + 381439619649.253 pi');
210+
expect(expr.round_numbers_to_precision_plus_decimals(100,1).equals(
211+
me.fromText('3/7exp(1) + 381439619649.253 pi'))).toBeTruthy();
212+
expect(expr.round_numbers_to_precision_plus_decimals(14,1).equals(
213+
me.fromText('3/7exp(1) + 381439619649.25 pi'))).toBeTruthy();
214+
expect(expr.round_numbers_to_precision_plus_decimals(10,1).equals(
215+
me.fromText('3/7exp(1) + 381439619649.3 pi'))).toBeTruthy();
216+
expect(expr.round_numbers_to_precision_plus_decimals(4,1).equals(
217+
me.fromText('3/7exp(1) + 381439619649.3 pi'))).toBeTruthy();
218+
219+
});
220+
221+
222+
});
223+
224+
150225
});

0 commit comments

Comments
 (0)