Skip to content

Commit

Permalink
Set default value of precisionRounding to 10 (#1456)
Browse files Browse the repository at this point in the history
* Set default value for precisionRounding parameter to 10

* Add link about the limitations of the javascript arithmetics to the specifications-and-limits guide

* Add changelog entry and the migration guide

* Adjust unit tests
  • Loading branch information
sequba authored Nov 13, 2024
1 parent 4ca2f87 commit 2a5cd84
Show file tree
Hide file tree
Showing 23 changed files with 60 additions and 46 deletions.
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

### Changed

- **Breaking change**: Change ES module build to use `mjs` files and `exports` property in `package.json` to make importing language files possible in Node environment. [#1344](https://github.com/handsontable/hyperformula/issues/1344)
- **Breaking change**: Removed the `binarySearchThreshold` configuration option. [#1439](https://github.com/handsontable/hyperformula/issues/1439)
- **Breaking change**: Changed ES module build to use `mjs` files and `exports` property in `package.json` to make importing language files possible in Node environment. [#1344](https://github.com/handsontable/hyperformula/issues/1344)
- **Breaking change**: Changed the default value of the `precisionRounding` configuration option to `10`. [#1300](https://github.com/handsontable/hyperformula/issues/1300)
- Make methods `simpleCellAddressToString` and `simpleCellRangeToString` more logical and easier to use. [#1151](https://github.com/handsontable/hyperformula/issues/1151)

### Removed

- **Breaking change**: Removed the `binarySearchThreshold` configuration option. [#1439](https://github.com/handsontable/hyperformula/issues/1439)

## [2.7.1] - 2024-07-18

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/basic-usage/example1.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ console.log(
const tableData = [['10', '20', '=SUM(A1,B1)']];
// Create an empty HyperFormula instance.
const hf = HyperFormula.buildEmpty({
precisionRounding: 10,
precisionRounding: 9,
licenseKey: 'gpl-v3',
});

Expand Down
2 changes: 1 addition & 1 deletion docs/examples/basic-usage/example1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const tableData = [['10', '20', '=SUM(A1,B1)']];

// Create an empty HyperFormula instance.
const hf = HyperFormula.buildEmpty({
precisionRounding: 10,
precisionRounding: 9,
licenseKey: 'gpl-v3',
});

Expand Down
2 changes: 1 addition & 1 deletion docs/guide/configuration-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ static method called to initiate a new instance of HyperFormula.
// define options
const options = {
licenseKey: 'gpl-v3',
precisionRounding: 10,
precisionRounding: 9,
nullDate: { year: 1900, month: 1, day: 1 },
functionArgSeparator: '.'
};
Expand Down
10 changes: 10 additions & 0 deletions docs/guide/migration-from-2.x-to-3.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,13 @@ We tested the changes with the most popular bundlers and frameworks. If you use
## Removal of the `binarySearchThreshold` configuration option (deprecated since version 1.1.0)

The `binarySearchThreshold` has no effect since version 1.1.0. If your codebase still uses this option, please remove it.

## Change in the default value of the `precisionRounding` configuration option

HyperFormula 3.0.0 introduces a change in the default value of the `precisionRounding` configuration option. The new default value is `10`. If you want to keep the old behavior, set the `precisionRounding` option to `14` in the HyperFormula configuration:

```javascript
const hf = HyperFormula.buildEmpty({
precisionRounding: 14
});
```
24 changes: 12 additions & 12 deletions docs/guide/specifications-and-limits.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,15 @@ is running on.

## Calculation limits

| Feature | Maximum limit |
| :--- | :--- |
| Default number precision | 15 digits (inherited from JavaScript) |
| Smallest magnitude allowed negative number | -5E-324 (inherited from JavaScript) |
| Smallest magnitude allowed positive number | 5E-324 (inherited from JavaScript) |
| Largest magnitude allowed positive number | 1.79E+308 (inherited from JavaScript) |
| Largest magnitude allowed negative number | -1.79E+308 (inherited from JavaScript) |
| Length of a single formula's contents | Limited by system resources (JavaScript) |
| Number of iterations | Not supported |
| Arguments in function | Limited by system resources (JavaScript) |
| Number of cross-sheet dependencies | Limited by system resources (JavaScript) |
| Number of dependencies in a single cell | Limited by system resources (JavaScript) |
| Feature | Maximum limit |
|:-------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Number precision | ~15 significant digits (with all the [limitations of the JavaScript floating-point arithmetics](https://patrickkarsh.medium.com/why-math-is-hard-in-javascript-floating-point-precision-in-javascript-41706aa7a89d)). HyperFormula rounds the operation results according to the [precisionRounding](../api/interfaces/configparams.html#precisionrounding) configuration option. |
| Smallest magnitude allowed negative number | -5E-324 (inherited from JavaScript) |
| Smallest magnitude allowed positive number | 5E-324 (inherited from JavaScript) |
| Largest magnitude allowed positive number | 1.79E+308 (inherited from JavaScript) |
| Largest magnitude allowed negative number | -1.79E+308 (inherited from JavaScript) |
| Length of a single formula's contents | Limited by system resources (JavaScript) |
| Number of iterations | Not supported |
| Arguments in function | Limited by system resources (JavaScript) |
| Number of cross-sheet dependencies | Limited by system resources (JavaScript) |
| Number of dependencies in a single cell | Limited by system resources (JavaScript) |
2 changes: 1 addition & 1 deletion src/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class Config implements ConfigParams, ParserConfig {
nullDate: {year: 1899, month: 12, day: 30},
parseDateTime: defaultParseToDateTime,
precisionEpsilon: 1e-13,
precisionRounding: 14,
precisionRounding: 10,
smartRounding: true,
stringifyDateTime: defaultStringifyDateTime,
stringifyDuration: defaultStringifyDuration,
Expand Down
4 changes: 2 additions & 2 deletions src/ConfigParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,8 @@ export interface ConfigParams {
* Setting `precisionRounding` too low can cause large numbers' imprecision
* (for example, with `precisionRounding` set to `4`, 100005 becomes 100010).
*
* We recommend setting `precisionRounding` to a value between `10` and `14`.
* @default 14
* Setting precisionRounding too high will expose the floating-point calculation errors. For example, with `precisionRounding` set to `15`, `0.1 + 0.2` results in `0.3000000000000001`.
* @default 10
* @category Number
*/
precisionRounding: number,
Expand Down
8 changes: 4 additions & 4 deletions test/CellValueExporter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ describe('rounding', () => {
it('with rounding', () => {
const config = new Config()
const cellValueExporter = new Exporter(config, namedExpressionsMock, sheetIndexMock, lazilyTransforminService)
expect(cellValueExporter.exportValue(1.0000000000001)).toBe(1.0000000000001)
expect(cellValueExporter.exportValue(-1.0000000000001)).toBe(-1.0000000000001)
expect(cellValueExporter.exportValue(1.000000000000001)).toBe(1)
expect(cellValueExporter.exportValue(-1.000000000000001)).toBe(-1)
expect(cellValueExporter.exportValue(1.000000001)).toBe(1.000000001)
expect(cellValueExporter.exportValue(-1.000000001)).toBe(-1.000000001)
expect(cellValueExporter.exportValue(1.00000000001)).toBe(1)
expect(cellValueExporter.exportValue(-1.00000000001)).toBe(-1)
expect(cellValueExporter.exportValue(0.0000000000001)).toBe(0.0000000000001)
expect(cellValueExporter.exportValue(-0.0000000000001)).toBe(-0.0000000000001)
expect(cellValueExporter.exportValue(true)).toBe(true)
Expand Down
12 changes: 6 additions & 6 deletions test/config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,22 +375,22 @@ describe('Config', () => {
const dateAsNumber = 0.564969131944444

let engine = HyperFormula.buildFromArray([[dateAsString]], { timeFormats: ['hh:mm:ss'] })
expect(engine.getCellValue(adr('A1'))).toEqual(dateAsNumber)
expect(engine.getCellValue(adr('A1'))).toBeCloseTo(dateAsNumber, 11)

engine = HyperFormula.buildFromArray([[dateAsString]], { timeFormats: ['hh:mm:ss.s'] })
expect(engine.getCellValue(adr('A1'))).toEqual(dateAsNumber)
expect(engine.getCellValue(adr('A1'))).toBeCloseTo(dateAsNumber, 11)

engine = HyperFormula.buildFromArray([[dateAsString]], { timeFormats: ['hh:mm:ss.ss'] })
expect(engine.getCellValue(adr('A1'))).toEqual(dateAsNumber)
expect(engine.getCellValue(adr('A1'))).toBeCloseTo(dateAsNumber, 11)

engine = HyperFormula.buildFromArray([[dateAsString]], { timeFormats: ['hh:mm:ss.sss'] })
expect(engine.getCellValue(adr('A1'))).toEqual(dateAsNumber)
expect(engine.getCellValue(adr('A1'))).toBeCloseTo(dateAsNumber, 11)

engine = HyperFormula.buildFromArray([[dateAsString]], { timeFormats: ['hh:mm:ss.ssss'] })
expect(engine.getCellValue(adr('A1'))).toEqual(dateAsNumber)
expect(engine.getCellValue(adr('A1'))).toBeCloseTo(dateAsNumber, 11)

engine = HyperFormula.buildFromArray([[dateAsString]], { timeFormats: ['hh:mm:ss.sssss'] })
expect(engine.getCellValue(adr('A1'))).toEqual(dateAsNumber)
expect(engine.getCellValue(adr('A1'))).toBeCloseTo(dateAsNumber, 11)
})
})

Expand Down
2 changes: 1 addition & 1 deletion test/interpreter/function-acot.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe('Function ACOT', () => {
])

expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-0.785398163397448)
expect(engine.getCellValue(adr('B2'))).toEqual(1.5707963267949)
expect(engine.getCellValue(adr('B2'))).toBeCloseTo(1.5707963267949, 10)
})

it('errors propagation', () => {
Expand Down
2 changes: 1 addition & 1 deletion test/interpreter/function-bitlshift.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ describe('function BITLSHIFT', () => {
['=BITLSHIFT(2, 47)'],
])

expect(engine.getCellValue(adr('A1'))).toEqual(140737488355328)
expect(engine.getCellValue(adr('A1'))).toBeCloseTo(140737488355328, -4)
expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.BitshiftLong))
})

Expand Down
2 changes: 1 addition & 1 deletion test/interpreter/function-bitrshift.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ describe('function BITRSHIFT', () => {
['=BITRSHIFT(2, -47)'],
])

expect(engine.getCellValue(adr('A1'))).toEqual(140737488355328)
expect(engine.getCellValue(adr('A1'))).toBeCloseTo(140737488355328, -4)
expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.BitshiftLong))
})

Expand Down
2 changes: 1 addition & 1 deletion test/interpreter/function-combina.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe('Function COMBINA', () => {
expect(engine.getCellValue(adr('A6'))).toBe(20030010)
expect(engine.getCellValue(adr('A7'))).toBe(635745396)
expect(engine.getCellValue(adr('A8')) as number / 1.8523520317769801e+115).toBeCloseTo(1, 6)
expect(engine.getCellValue(adr('A9'))).toBe(325949656825)
expect(engine.getCellValue(adr('A9'))).toBeCloseTo(325949656825, -2)
expect(engine.getCellValue(adr('A10')) as number / 1.41325918108873e+308).toBeCloseTo(1, 6)
})

Expand Down
2 changes: 1 addition & 1 deletion test/interpreter/function-csch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe('Function CSCH', () => {
it('happy path', () => {
const engine = HyperFormula.buildFromArray([['=CSCH(1)']])

expect(engine.getCellValue(adr('A1'))).toBe(0.850918128239322)
expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.850918128239322, 11)
})

it('when value not numeric', () => {
Expand Down
2 changes: 1 addition & 1 deletion test/interpreter/function-decimal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ describe('Function DECIMAL', () => {
[`=DECIMAL(\"${tooLongNumber}\", 2)`],
])

expect(engine.getCellValue(adr('A1'))).toEqual(5.78960446186581e+76)
expect(engine.getCellValue(adr('A1'))).toBeCloseTo(5.78960446186581e+76, -66)
expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotHex))
})

Expand Down
4 changes: 2 additions & 2 deletions test/interpreter/function-hex2dec.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ describe('function HEX2DEC', () => {
expect(engine.getCellValue(adr('A6'))).toEqual(11574629)
expect(engine.getCellValue(adr('A7'))).toEqual(253233380)
expect(engine.getCellValue(adr('A8'))).toEqual(185061383)
expect(engine.getCellValue(adr('A9'))).toEqual(549755813887)
expect(engine.getCellValue(adr('A9'))).toBeCloseTo(549755813887, -1)
expect(engine.getCellValue(adr('A10'))).toEqual(-54444247247)
expect(engine.getCellValue(adr('A11'))).toEqual(-1)
})
Expand Down Expand Up @@ -84,6 +84,6 @@ describe('function HEX2DEC', () => {
])

expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotHex))
expect(engine.getCellValue(adr('A2'))).toEqual(513113223378)
expect(engine.getCellValue(adr('A2'))).toBeCloseTo(513113223378, -1)
})
})
4 changes: 2 additions & 2 deletions test/interpreter/function-pv.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ describe('Function PV', () => {

expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-1891.39256, 6)
expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY)
expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-2140.081155, 6)
expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-2140.081155, 5)
expect(engine.getCellValue(adr('C1'))).toBeCloseTo(-2177.909007, 6)
expect(engine.getCellValue(adr('A2'))).toBeCloseTo(-1.01010101010099e+50, 6)
expect(engine.getCellValue(adr('A2'))).toBeCloseTo(-1.01010101010099e+50, -39)
expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO))
expect(engine.getCellValue(adr('C2'))).toBeCloseTo(-400, 6)
expect(engine.getCellValue(adr('A3'))).toEqual(-2400)
Expand Down
2 changes: 1 addition & 1 deletion test/interpreter/function-sec.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ describe('Function SEC', () => {
[1.57079632679486, '=SEC(A1)'],
])

expect(engine.getCellValue(adr('B1'))).toBeCloseTo(27249001701268.1)
expect(engine.getCellValue(adr('B1'))).toBeCloseTo(27249001701268.1, -3)
})

it('errors propagation', () => {
Expand Down
4 changes: 2 additions & 2 deletions test/interpreter/function-seriessum.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ describe('Function SERIESSUM', () => {
[1, 2, 3, 4]
])

expect(engine.getCellValue(adr('A1'))).toBeCloseTo(147347.41562949, 6)
expect(engine.getCellValue(adr('A2'))).toBeCloseTo(168708.537245456, 6)
expect(engine.getCellValue(adr('A1'))).toBeCloseTo(147347.41562949, 5)
expect(engine.getCellValue(adr('A2'))).toBeCloseTo(168708.537245456, 5)
})

it('propagates errors', () => {
Expand Down
2 changes: 1 addition & 1 deletion test/interpreter/function-stdeva.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('Function STDEVA', () => {
const engine = HyperFormula.buildFromArray([
['=STDEVA(2, 3)'],
])
expect(engine.getCellValue(adr('A1'))).toEqual(0.707106781186548)
expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.707106781186548, 11)
})

it('should coerce explicit argument to numbers', () => {
Expand Down
2 changes: 1 addition & 1 deletion test/interpreter/function-t.dist.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe('Function T.DIST', () => {
['=T.DIST(1, 0.9, FALSE())'],
])

expect(engine.getCellValue(adr('A1'))).toEqual(0.159154942198517)
expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.159154942198517, 11)
//product #2 returns different error
expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall))
})
Expand Down
2 changes: 1 addition & 1 deletion test/interpreter/precision.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ describe('tests', () => {
['0.000123456789', '1', '=A1+B1'],
], {smartRounding: true})

expect(engine.getCellValue(adr('C1'))).toEqual(1.000123456789)
expect(engine.getCellValue(adr('C1'))).toBeCloseTo(1.000123456789, 10)
})

it('addition of small numbers with smartRounding #2', () => {
Expand Down

0 comments on commit 2a5cd84

Please sign in to comment.