@rinxun/numeric-calculator 1.0.7 → 1.0.8
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/README.md +11 -2
- package/index.d.ts +13 -1
- package/index.js +10 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -142,7 +142,7 @@ const result = new NC(-1000).divide(100, '20', 2).toPrecision(); // -1000/100/20
|
|
|
142
142
|
|
|
143
143
|
|
|
144
144
|
|
|
145
|
-
<h4>inst.round(fractionDigits?: number)</h4>
|
|
145
|
+
<h4>inst.round(fractionDigits?: number, options?: { snap?: boolean })</h4>
|
|
146
146
|
|
|
147
147
|
``` typescript
|
|
148
148
|
const result = new NC().round(3); // 0
|
|
@@ -151,6 +151,15 @@ const result = new NC(7.65).times(16.5).round(2); // => 7.65 * 16.5 = 126.225
|
|
|
151
151
|
const result = new NC(26.05).times(16.5).round(2); // => 26.05 * 16.5 = 429.825 ≈ 429.83
|
|
152
152
|
```
|
|
153
153
|
|
|
154
|
+
`options.snap` (default `false`) makes rounding tolerant to binary floating-point representation dust. After plain float arithmetic upstream, a true `95.015` can arrive as `95.01499999999942`, and exact-decimal rounding of the *represented* value then rounds down, disagreeing with the true value. With `snap`, the amplified value is nudged by a few ULPs relative to its own magnitude before `Math.round`, so only values sitting exactly on a rounding boundary are affected — everything beyond a few ULPs of the boundary keeps its exact-decimal result, and there is no magic number to tune:
|
|
155
|
+
|
|
156
|
+
``` typescript
|
|
157
|
+
const dust = 2959.9649999999997; // true value 2959.965, distorted by float subtraction upstream
|
|
158
|
+
const result = new NC(dust).round(2); // 2959.96 — exact decimal of the represented value
|
|
159
|
+
const result = new NC(dust).round(2, { snap: true }); // 2959.97 — matches the true half-up value
|
|
160
|
+
const result = new NC(719.6446).round(2, { snap: true }); // 719.64 — values beyond a few ULPs of the boundary are untouched
|
|
161
|
+
```
|
|
162
|
+
|
|
154
163
|
|
|
155
164
|
|
|
156
165
|
<h4>inst.toPrecision(precision?: number)</h4>
|
|
@@ -187,4 +196,4 @@ const result = new NC(10).plus(new NC().times(2, 4), '6').divide(new NC(12).minu
|
|
|
187
196
|
|
|
188
197
|
<h2>Changelog</h2>
|
|
189
198
|
|
|
190
|
-
If you have recently updated, please read the [changelog](https://github.com/rinxun/numeric-calculator/releases) for details of what has changed.
|
|
199
|
+
If you have recently updated, please read the [changelog](https://github.com/rinxun/numeric-calculator/releases) for details of what has changed.
|
package/index.d.ts
CHANGED
|
@@ -3,6 +3,17 @@ declare type IConfig = {
|
|
|
3
3
|
fractionDigits?: number;
|
|
4
4
|
enableCheckBoundary?: boolean;
|
|
5
5
|
};
|
|
6
|
+
declare type IRoundOptions = {
|
|
7
|
+
/**
|
|
8
|
+
* @description when true, nudges the amplified value by a few ULPs relative
|
|
9
|
+
* to its own magnitude before rounding, so that binary floating-point
|
|
10
|
+
* representation dust does not flip values sitting exactly on a rounding
|
|
11
|
+
* boundary. Use it when the input may come from plain float arithmetic
|
|
12
|
+
* upstream, e.g. a true 95.015 arriving as 95.01499999999942. Defaults to
|
|
13
|
+
* false, which keeps the exact-decimal behavior of the represented value.
|
|
14
|
+
*/
|
|
15
|
+
snap?: boolean;
|
|
16
|
+
};
|
|
6
17
|
declare type IOperand = Calculator | number | string;
|
|
7
18
|
/**
|
|
8
19
|
* @description Calculate addition, subtaction, multiplication, division precisely, for tackling the precision issue
|
|
@@ -134,8 +145,9 @@ declare class Calculator {
|
|
|
134
145
|
/**
|
|
135
146
|
*
|
|
136
147
|
* @param {number} fractionDigits default to 2, should be in the range 0 - 20
|
|
148
|
+
* @param {IRoundOptions} options rounding options, see {@link IRoundOptions.snap}
|
|
137
149
|
* @returns {number} rounded result
|
|
138
150
|
*/
|
|
139
|
-
round(fractionDigits?: number): number;
|
|
151
|
+
round(fractionDigits?: number, options?: IRoundOptions): number;
|
|
140
152
|
}
|
|
141
153
|
export default Calculator;
|
package/index.js
CHANGED
|
@@ -323,13 +323,21 @@ var Calculator = /** @class */ (function () {
|
|
|
323
323
|
/**
|
|
324
324
|
*
|
|
325
325
|
* @param {number} fractionDigits default to 2, should be in the range 0 - 20
|
|
326
|
+
* @param {IRoundOptions} options rounding options, see {@link IRoundOptions.snap}
|
|
326
327
|
* @returns {number} rounded result
|
|
327
328
|
*/
|
|
328
|
-
Calculator.prototype.round = function (fractionDigits) {
|
|
329
|
+
Calculator.prototype.round = function (fractionDigits, options) {
|
|
329
330
|
var magnification = Math.pow(10, fractionDigits || this._fractionDigits || 2);
|
|
330
331
|
var amplifyingResult = this.processTimes(this._VALUE || 0, magnification);
|
|
331
332
|
this.checkBoundary(amplifyingResult);
|
|
332
|
-
|
|
333
|
+
// Dust observed in real pipelines reaches a few dozen ULPs (each float op
|
|
334
|
+
// distorts by at most 0.5 ULP), so 64x headroom. This stays safely below
|
|
335
|
+
// half an amplified unit at any magnitude (it scales with the value), and
|
|
336
|
+
// assumes inputs are meaningful well beyond ULP precision.
|
|
337
|
+
var tolerance = (options === null || options === void 0 ? void 0 : options.snap)
|
|
338
|
+
? Math.abs(amplifyingResult) * Number.EPSILON * 64
|
|
339
|
+
: 0;
|
|
340
|
+
return this.processDivide(Math.round(amplifyingResult + tolerance), magnification);
|
|
333
341
|
};
|
|
334
342
|
return Calculator;
|
|
335
343
|
}());
|