Skip to content

Commit

Permalink
Financial math lesson.
Browse files Browse the repository at this point in the history
  • Loading branch information
lucanicoladebiasi committed Oct 24, 2024
1 parent 29be336 commit 53785a6
Showing 1 changed file with 57 additions and 57 deletions.
114 changes: 57 additions & 57 deletions src/1.Hello_World/FinancialMath.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,55 @@
# Financial Math

JavaScript (JS) was designed as front-end language, providing the simpler data type to cope with the need
of rendering, initially just the `string` type to express texts and `number` and `bigint` types to compute math.
of rendering, initially just the `string` type to express texts and `number` or `bigint` types to compute math.
JS was not designed to be strongly typed language, but the language was designed to delegate at runtime
the assessment of the type to represent any variable according its content.
When the success of JS as 'browser language' made it popular as a more general purpose language,
the need to pinpoint the data type of variable pushed the creation of TypeScript (TS)
as a language derived by and compatible with JS making explicit the type definition of variables,
and many libraries to provide specialized types to overcome the limits of JS.
as a language derived by and compatible with JS. TS makes explicit the type definition of variables.

## Number Type Limits

The JS `number` type implements the 64
bits [Double Precision Floating Point](https://en.wikipedia.org/wiki/Double-precision_floating-point_format)
representation as the `double` type common in *C#*, *Java?Kotlin* and *Rust/Zig* programming languages.
representation as the `double` type common in *C#*, *Java/Kotlin* and *Rust/Zig* programming languages.
This representation uses 8 bytes to approximate the value as follows

- 1 bit to represent the value **sign**,
- 11 bits to represent an **exponent**, one bit for the exponent sign and 2<sup>10</sup> = 1024 as exponent value,
- 11 bits to represent an **exponent**, one bit for the exponent sign and 2<sup>10</sup> = 0-1024 as exponent range
value,
- 52 bits to represent an integer - named **significand** multiplied for the above **exponent** and **sign**,
2<sup>52</sup> is roughly equal to 4500000000000000 (45 followed by fourteen zeros).

Any value can't be represented exactly as a product of the integers **significand** * **2<sup>exponent</sup>** * **sign
**
is approximated with less precision for values very big or very small fraction, positive or negative doesn't matter.
Any value can't be represented exactly as a product of the integers **significand** × **2<sup>exponent</sup>**
× **sign** is approximated with less precision for values very big or very small fraction, positive or negative doesn't
matter.

Actually, IEEE-754 represents precisely values in the range ±2.23×10<sup>−308</sup> to ±1.80×10<sup>308</sup> in 16
decimal digits comprehensive of the integer and fractional parts.

It's important to anticipate that due to the finite quantity of computer memory and the binary nature of electronic
logic,
computers can only represent values as polynomial expressions of powers of 2. Hence, any rational value not the result
of a ratio between two values made of powers of two, like **1/3** returning "zero dot an infinite string of three" (also
named '[repeating decimal](https://en.wikipedia.org/wiki/Repeating_decimal) three'), they
It's important to anticipate that, due to the finite quantity of computer memory and the binary nature of electronic
logic, computers can only represent values as polynomial expressions of powers of 2. Hence, any rational value not the
result of a ratio between two values made of powers of two, like **1/3** returning "zero dot an infinite string of
three" (also named '[repeating decimal](https://en.wikipedia.org/wiki/Repeating_decimal) three'), they
can only be approximated. The quantity of memory to represent precisely 1/3 is infinite.
It is worth to remember any [irrational number](https://en.wikipedia.org/wiki/Irrational_number),
$\sqrt{2}$ and π the most famous, can be only approximated in a computer architecture
$\sqrt{2}$ and π are the most famous, can be only approximated in a computer architecture
because writing an irrational value needs an infinite number of digits.

From what explained above, albeit the IEEE-754 allows to express a wide range of values, the 16 decimal digit
precision is not sufficient for many purposes, like financial math, approximating values to cents (two digits),
the biggest value representable in any currency is 10<sup>14</sup> hence at most 10 trillions (10<sup>12</sup>)
precision is not sufficient for many purposes, like financial math. For example, approximating values to cents (two
digits), the biggest value representable in any currency is 10<sup>14</sup> hence at most 10 trillions (10<sup>12</sup>)
of the currency units. Using more digits to represent fraction of cents to compute the
[compound interest](https://en.wikipedia.org/wiki/Compound_interest), the remaining digits allow to express
values in the range of billions, a too small range.
values in the range of billions, this is a too small range.

## Cryptocurrency Economy Math

Cryptocurrencies like [BTC](https://en.wikipedia.org/wiki/Bitcoin), [ETH](https://en.wikipedia.org/wiki/Ethereum) or
VeChain [VET](https://docs.vechain.org/introduction-to-vechain/dual-token-economic-model/vechain-vet) requires
18 decimal digits to express the fractional part their nomination currency unit, plus the digits required to
express the units, hence IEEE-754 is too coarse for the purpose of cryptocurrency economy.
18 decimal digits to express the fractional part of their nomination currency unit, plus the digits required to
express the units, hence the IEEE-754 precision is too coarse for the purpose of cryptocurrency economy.

The need of 18 digits for the fractional part of a cryptocurrency is suggested by the idea to express
cryptocurrency values as an integer value of its atomic smallest subunit named **wei**,
Expand All @@ -61,17 +60,17 @@ to honor the computer engineer [Wei Dai](https://en.wikipedia.org/wiki/Wei_Dai).
JS provides the
[`bigint`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
type, characterized to be a variable length type,
as [`string`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String),
`bigint` expands to include all the bits needed to express any integer value, `bigint` values are limited by
available memory, hence practically unlimited.
as [`string`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) is.
The `bigint` type expands to include all the bits needed to express any integer value,
`bigint` values are limited by available memory of the JS runtime.
However, if `bigint` is ideal to express currency values, it doesn't work well for any mathematics operation
involving the concept of ratio, as the division and root. Between integers, 1/3=0, not ideal when the same
result computed between real values returns 'repeating decimal' .3
result computed between real values returns 'repeating decimal' .3.

## FixedPointNumber Type

The VeChain SDK provides the `FixedPointNumber` class to provide an unrestricted representation of values,
as `bigint` but supporting the concept of 'ratio' and rational numbers and allowing to define the precision
as `bigint` but supporting the concept of 'ratio' and rational numbers, allowing to define the precision
of the approximation of irrational values.

The most important characteristic of the `FixedPointNumber` class is the algorithms it provides are purely based
Expand All @@ -80,7 +79,7 @@ a base type of the JS language.

### bignumber.js comparison

In JS there are other libraries addressing the same problem to represent with adjustable precision.
In JS there are other libraries addressing the same problem to represent values with adjustable precision.
Among them, it is worth to name [**bignumber.js**](https://mikemcl.github.io/bignumber.js/).
The `FixedPointNumber` class was developed using **bignumber.js** as reference and benchmark:
most of the methods provided by the `FixedPointNumber` class have the same names and signature
Expand All @@ -91,15 +90,15 @@ developed for **bignumber.js** to run based on `FixedPointNumber`.
The reason the VeChain SDK provides its `FixedPointNumber` class instead of using **bignumber.js** is because
the algorithms implemented in the class allows a true unrestricted precision, just limited by the available memory,
those are simpler to verify manually, the source code links the abstract descriptions in math literature hence
everyone competent can verify those are correctly implement each non-trivial algorithm like the division,
the power exponentiation and square roots and strictly based on `bigint`.
everyone competent can verify those are correctly implemented for each non-trivial algorithm like the division,
the power exponentiation and square roots. All algorithms are strictly based on `bigint`.

The two libraries differ in the way they represent values.

The **bignumber.js** library encodes each value
similarly as IEEE-754 `number` using **significand**, **exponent** and **sign** properties, with the difference
the **significand** is an array of `numbers` (8 bytes per element) encoding each digit of the value to approximate,
for example the value -1.234567 × 10<sup>4</sup> is represented internally as
the **significand** is an array of `numbers` (8 bytes per element) encoding each digit of the value to approximate.
For example, the value -1.234567 × 10<sup>4</sup> is represented internally as

- **significand** = `[1,2,3,4,5,6,7]`
- **exponent** = `4`
Expand All @@ -109,10 +108,9 @@ The mention and comparison with **bignumber.js** is due because `FixedPointNumbe
the whole functionalities of the **bignumber.js** library. For example `FixedPointNumber` class supports
the square root operation, but it doesn't support yet the power to fractional exponent (n<sup>0.5/sup> = $\sqrt{n}$).

**_NOTE:_** VeChain uses and suggests to use [**bignumber.js**](https://mikemcl.github.io/bignumber.js/)
for the functionalities not included in the `FixedPointNumber` class. **bignuber.js** is not part of the SDK and must
be imported explicitely in any JS/TS project using it.

**_NOTE:_** VeChain uses and suggests to use [**bignumber.js**](https://mikemcl.github.io/bignumber.js/)
for the functionalities not included in the `FixedPointNumber` class. The **bignumber.js** library
is not part of the SDK and must be imported explicitly in any JS/TS project using it.

### FixedPointNumber math

Expand All @@ -127,7 +125,7 @@ For this reason the `FixedPointNumber` exposes two properties, both of `bigint`

- `scaledValue` expresses the original value
- if integer the integer itself,
- if not integer the value multiplied by 10<sup>`fractionalDigits`</sum>
- if not integer the value multiplied by 10<sup>`fractionalDigits`</sum>;
- `fractionalDigit` expresses the number of decimal digits required to write the fractional part of the value, it's `0`
for integers.

Expand All @@ -136,16 +134,16 @@ For example 1.234567 is encoded as
- `scaledvalue = 1234567` (as 1.234567 × 10<sup>6</sup>),
- `fractionalDigit = 6`.

The expression 'fixed point' number means a `FixedPointNumber` objects use `fractionalDigit` to fix its
The expression 'fixed point number' means a `FixedPointNumber` objects uses `fractionalDigit` to fix its
precision and pinpoint where the decimal separator, (the `.` dot character by default) is placed when the value
is printed.

## FixedPointNumber In Action

The `FixedPointNumber` class is part of the to the VeChain Data Model** hence, the .`of(exp: bigint | number | string)`
The `FixedPointNumber` class is part of the VeChain Data Model** hence, the .`of(exp: bigint | number | string)`
method is the standard way to create a new object from the `exp` argument.

In the following example shows the same value represented by two different type expressions create two equivalent
The following example shows the same value, represented by two different type expressions, creates two equivalent
objects and how the `.bi` and `.n` properties behave.

```typescript
Expand All @@ -170,25 +168,27 @@ console.log(`Cast FPN value to bigint is ${x.bi}.`);
The arithmetic of the `FixedPointNumber` class is trivial for addition, subtraction and multiplication, the game
becomes interesting when the division is involved. Recalling the well known ratio 1/3, the result of the division
between `bigint` is zero and from above explanation, we know the object represents the internal operand as integers.
How `FixedPointNumber` class solves the challenge to get a rational result from integer division?

The `FixedPointNumber` class uses the `fractionalDigits` property to scale up and down the `scaledValue`
appropriately to a precision fine enough, by default when integers are involved the division, logarithm and root
operations internally scales up the value representation of 10<sup>20</sup> imposing an internal minimal precision
of 20 decimal digits, to be sure the result is accurate for the 18 digits the cryptocurrency math needs to represent any
value in terms of **wei** atomic subunits and have two digits more to approximate the less significant **wei** digit.
appropriately to a precision fine enough, by default when integers are involved in the division, logarithm and root
operations internally, the class' algorithms scale up the value representation of 10<sup>20</sup>
imposing an internal minimal precision of 20 decimal digits,
to be sure the result is accurate for the 18 digits the cryptocurrency math needs to represent any
value in terms of **wei** atomic subunits, have two spare digits more to approximate the less significant **wei** digit.

We will see later the `.dp(decimalPlaces: bigint | number)` method allows to fix the minimal required precision,
when two rational numbers are involved in a arithmetic operations risking leading to a loss of precision,
the internal math is caled uop to the double of the fractional digits of argument having the higher
`fractionalDigit` value.
We will see later the `.dp(decimalPlaces: bigint | number)` method allows to fix the minimal required precision.
When two rational numbers are involved in arithmetic operations risking leading to a loss of precision,
the internal math is scaled up to the double of the fractional digits of argument having the higher
`fractionalDigit` value using the `.dp` method.

For the 1/3 ratio the division is made between
10000000000000000000000000000000000000000/30000000000000000000000000000000000000000
hence the integer scaled up by 10<sup>default 20 fractional digit precision multiplied 2 times</sup>:
10000000000000000000000000000000000000000 / 30000000000000000000000000000000000000000,
hence the integer is scaled up by 10<sup>default 20 fractional digit precision multiplied 2 times</sup>:
the division will return something like 0.33333333333333333333????????????????????
where the question mark acks as a placeholder for digits should be 3, but it will diverge from 3 as the digit is closer
where the question mark acts as a placeholder for digits should be 3, but it will diverge from 3 as the digit is closer
to the less meaningful digit. As recalled many times, computers must approximate at some point.
Therm, the 40 digits internal precision is scaled down 10<sup>default 20 fractional digit</sup> preserving
Then, the 40 digits internal precision is scaled down 10<sup>default 20 fractional digit</sup> preserving
an accurate results for the wished 20 fractional digits' precision.

The elegance of `FixedPointNumber` implementation is the precision is fixed, but expand and shrink internally to
Expand All @@ -214,12 +214,13 @@ For sake of comprehension, the snipped above prints
```

As expected JS `number` type math is precise in 16 digits included the integer `0` and the following fifteen `3`, then
diverge.
it diverges.

The **bignumber.js** math returns a 20 fractional digits ratio more precise than JS math but approximate for the last 4
digits. It's a good practice using **bignumber.js** to set a decimal precision with the same
`.dp(decimalPlaces: bigint | number)` in common with the `FixedPointNumber` class to a number of digits grater than
the minimal needed, then discard the last one where precision diverge. For example, needing at least 20 decimal
digits.
It's a good practice using **bignumber.js** to set a decimal precision, calling the same
`.dp(decimalPlaces: bigint | number)` method in common with the `FixedPointNumber` class, to a number of digits greater
than the minimal needed, then discard the last ones where precision diverge. For example, needing at least 20 decimal
precision, set `.dp(25)` and discard the last five digits.

The SDK math returns the value closer to the theoretical real number ratio.
Expand Down Expand Up @@ -248,7 +249,7 @@ The `FixedPointNumber` is more accurate regarding divisions.

### Square root challenge

The next example computes the square root on few natural numbers, let's see how JS, **bignumber.js** and
The next example computes the square root of few natural numbers, let's see how JS, **bignumber.js** and
`FixedPointNumber` class behave.

```typescript
Expand Down Expand Up @@ -288,11 +289,10 @@ The code prints the table

where the divergence between **bignumber.js** and the `FixedPointNumber` is limited to the last less significant digit.

Once more is worth to repeat ther is not an always ideal way to approximate real numbers to the binary representation
Once more, is worth to repeat there is not an always ideal way to approximate real numbers to the binary representation
computers provide, it's worth to repeat the previous experiment forcing the precision to 80 fractional digits, once more
**bignumber.js** converges to a result having less digits, `FixedPointNumber` math keep computing until the 80<sup>
th</sup>
digit internally resolving the computation with 160 digits.
th</sup> digit, internally resolving the computation with 160 digits.

The code

Expand Down

0 comments on commit 53785a6

Please sign in to comment.