Skip to content

Commit

Permalink
Merge remote-tracking branch 'remotes/origin/develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
CalionVarduk committed Jun 23, 2020
2 parents 9e2a57c + 357296f commit adc6271
Show file tree
Hide file tree
Showing 10 changed files with 120 additions and 26 deletions.
96 changes: 96 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,99 @@ This project contains a lightweight fixed-point number arithmetic.
If you are using `npm`, then simply run the `npm install frl-ts-arithmetic` CLI command to get the latest version.

If you are using `yarn`, then go with the `yarn add frl-ts-arithmetic` command.

## B. Fixed-point numbers

[Fixed](https://github.com/CalionVarduk/ts-arithmetic/blob/develop/src/fixed.ts#L63) class represents a [fixed-point number](https://en.wikipedia.org/wiki/Fixed-point_arithmetic). It allows to perform calculations in a fixed-point arithmetic.

The `Fixed` class contains all basic mathematical operations, such as `add`, `subtract`, `multiply`, `divide` and `modulo`. Let's check out a few examples:
```typescript
// creates a new fixed-point number with a precision of 2 fractional, decimal digits
// equal to 0
const fixed = new Fixed(2);

// adds another fixed-point number that represents
// a value of 3.4, with a precision of 4 fractional, decimal digits
// after this operation, our 'fixed' variable will be equal to 3.4
// or, to be a bit closer to how the number is actually stored, 340/100
fixed.add(Fixed.FromNumber(3.4, 4));

// subtracts another fixed-point number
// after this operation, 'fixed' variable will be equal to 1.15
// or 115/100
fixed.subtract(Fixed.FromNumber(2.25, 2));

// multiplies by another fixed-point number
// after this operation, 'fixed' variable will be equal to 13.23
// or 1323/100
// note, that the real result is actually 1.15 * 11.5 = 13.225
// however, since 'fixed' has a precision of 2, it has to round the result
fixed.multiply(Fixed.FromNumber(11.5, 1));

// divides by another fixed-point number
// after this operation, 'fixed' variable will be equal to 26.46
// or 2646/100
fixed.divide(Fixed.FromNumber(0.5, 1));

// calculates a remainder from a division by another fixed-point number
// after this operation, 'fixed' variable will be equal to 1.46
// or 146/100
fixed.modulo(Fixed.FromNumer(2.5, 2));
```

As you can see, fixed-point arithmetic doesn't save you from rounding errors (which will become very apparent after using multiplication, division and modulo operations a few times), however it behaves as if it actually performs calculations in base 10 arithmetic, which can save you quite a few headaches, when dealing with stuff like that:
```typescript
// floating-point arithmeric
let value = 0.1;
value += 0.2;

// returns false
const result = value === 0.3;

// fixed-point arithmetic
let fixedValue = Fixed.FromNumber(0.1, 1).addNumber(0.2);

// returns true
const fixedResult = fixedValue.equals(Fixed.FromNumber(0.3, 1));
```

This is one of the biggest advantages of the fixed-point arithmetic performed by the `Fixed` class over the floating-point arithmetic performed by the `number` type. It can be very useful, when dealing with e.g. currencies.

As hinted by the previous example, most operations have their `[x]Number` (e.g. `addNumber`) versions, which allow to pass a `number` as a parameter, instead of another `Fixed`-type object. Additionally, most operations have `[x]Normalized` (e.g. `addNormalized`) versions, which allow to pass a normalized `number` value as a parameter. In the case of the `Fixed` class, normalized means a raw value, which won't be converted because of the fixed-point number's precision. For example:
```typescript
// represents a 1.23 value
// or, more specifically, 12300/10000
const fixed = Fixed.FromNumber(1.23, 4);

// returns 12300
const norm = fixed.normalizedValue;

// adds 7 as a normalized value
// since 'fixed' variable's precision is 4
// then that 7 will actually represent a number equal to 0.0007
// after this operation, 'fixed' variable will be equal to 1.2307
// or 12307/10000
fixed.addNormalized(7);
```

`Fixed` also contains methods, that allow to compare values, like `equals` (which simply returns a `boolean` result) or `compareTo` (which returns a `number` result, `< 0` means, that the number is less than the parameter, `> 0` means, that the number is greater than the parameter and `=== 0` means, that the number is equal to the parameter).

It's also possible to convert a `Fixed` object to `number`, by simply calling the `toNumber` method. You can also perform a precision-cast by calling the `toPrecision` method, with a precision parameter of your choice, however, if you always need a new object, then call the static `Fixed.FromFixed` method instead, since `toPrecision` returns `this`, if the precision parameter is equal to the callee's precision.

Also, keep in mind, that `Fixed` object's have a limited range of safe values. Unlike `number`, which is implemented as a double precision floating-point number and has a max value equal to about `10^308`, the `Fixed` has a max safe value of `10^53 / 10^precision`, where allowed `precision` is in range `[0, 15]`. `Fixed` doesn't validate any overflow for the sake of efficiency, so it's up to you to decide when and how to check the value's correctness - `isSafe` property can help with that.

## C. Fixed-point math helpers

[FixedMath](https://github.com/CalionVarduk/ts-arithmetic/blob/develop/src/fixed.ts#L756) namespace contains a few helpful fixed-point arithmetic functions, such as:

- `Min` - returns the lowest value out of the two provided fixed-point numbers.
- `Max` - returns the highest value out of the two provided fixed-point numbers.
- `Abs` - returns an absolute value of the provided fixed-point number.
- `Truncate` - returns an integral part of the provided fixed-point number.
- `Fractional` - returns a fractional part of the provided fixed-point number.
- `Round` - rounds a fixed-point number to the provided precision.
- `Ceil` - returns a ceiling function result.
- `Floor` - returns a floor function result.
- `Sign` - returns `1`, if the provided fixed-point number is greater than 0, `-1`, if the provided number is less than 0, or `0`, if the provided number is equal to 0.
- `Random` - generates a random fixed-point number with the provided precision.
- `Sum` - performs a sum operation on the provided fixed-point numbers collection.
13 changes: 6 additions & 7 deletions jestconfig.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
{
"transform": {
"^.+\\.(t|j)sx?$": "ts-jest"
},
"testRegex": "/tests/.*\\.spec\\.(jsx?|tsx?)$",
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"]
}

"transform": {
"^.+\\.tsx?$": "ts-jest"
},
"testRegex": "/tests/.*\\.spec\\.(jsx?|tsx?)$",
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"]
}
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@
"ts-jest": "^25.2.1",
"tslint": "^5.16.0",
"typescript": "3.1.6",
"frl-ts-mocking": "^2.0.0"
"frl-ts-mocking": "^2.1.0"
}
}
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './core/fixed-precision';
export * from './core/fixed';
export * from './fixed-precision';
export * from './fixed';
4 changes: 2 additions & 2 deletions src/tests/fixed-math.spec.ts → tests/fixed-math.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Fixed, FixedMath } from '../core/fixed';
import { FixedPrecision } from '../core/fixed-precision';
import { Fixed, FixedMath } from '../src/fixed';
import { FixedPrecision } from '../src/fixed-precision';
import each from 'jest-each';

const precisions = [...Array(Fixed.MAX_PRECISION + 1).keys()];
Expand Down
8 changes: 4 additions & 4 deletions src/tests/fixed.spec.ts → tests/fixed.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Fixed } from '../core/fixed';
import { FixedPrecision } from '../core/fixed-precision';
import { Fixed } from '../src/fixed';
import { FixedPrecision } from '../src/fixed-precision';
import { partialMock, mock } from 'frl-ts-mocking/lib/mock';
import { IMockedMethodInfo } from 'frl-ts-mocking/lib/mocked-method-info.interface';
import each from 'jest-each';
import { partialMock, mock } from 'frl-ts-mocking/lib/core/mock';
import { IMockedMethodInfo } from 'frl-ts-mocking/lib/core/mocked-method-info.interface';

const precisions = [...Array(Fixed.MAX_PRECISION + 1).keys()];

Expand Down
5 changes: 2 additions & 3 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"strict": true,
"sourceMap": true
},
"include": ["src"],
"exclude": ["node_modules", "**/tests/*"]
"include": ["src", "tests"],
"exclude": ["node_modules", "tests", "lib"]
}

0 comments on commit adc6271

Please sign in to comment.