From 50e8cb889e4b2a469250bb20ebe20e44d68b03e7 Mon Sep 17 00:00:00 2001 From: lucanicoladebiasi Date: Thu, 24 Oct 2024 09:09:35 +0100 Subject: [PATCH 01/15] Financial math lesson... --- src/1.Hello_World/FinancialMath.md | 66 ++++++++++++++++++++++++++++-- src/4.Accounts/Account.mts | 36 ---------------- 2 files changed, 62 insertions(+), 40 deletions(-) delete mode 100644 src/4.Accounts/Account.mts diff --git a/src/1.Hello_World/FinancialMath.md b/src/1.Hello_World/FinancialMath.md index b101966..c8bed57 100644 --- a/src/1.Hello_World/FinancialMath.md +++ b/src/1.Hello_World/FinancialMath.md @@ -2,15 +2,73 @@ # Financial Math -JavaScript (JS) was designed as front-end language. +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` type 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 a 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. + +## Number 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. -This representation approximates the real value with less precision as the value moves to the edge of -the information can be represented in 8 bytes, +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 210 = 1024 as exponent value, +- 52 bits to represent an integer - named **significand** multiplied for the above **exponent** and **sign**, + 252 is roughly equal to 4500000000000000 (45 followed by fourteen zeros). + +Any value can't be represented exactly as a product of the integers **significand** * **2exponent** * **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−308 to ±1.80×10308 in 16 +decimal digits comprehensive of the integer and fractional parts. + +It's important to anticipate 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 power 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') +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 +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 1014 hence at most 10 trillions (1012) +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. + +## 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. + +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 sub-unit named **wei**, +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. +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 -String doesn't oblige to approximation. +## 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 +of the approximation of irrational values. https://mikemcl.github.io/bignumber.js/ diff --git a/src/4.Accounts/Account.mts b/src/4.Accounts/Account.mts deleted file mode 100644 index 8cff99f..0000000 --- a/src/4.Accounts/Account.mts +++ /dev/null @@ -1,36 +0,0 @@ -import {Address, Clause, Secp256k1, TransactionClause, VET, VTHO} from "@vechain/sdk-core"; -import {THOR_SOLO_URL, ThorClient} from "@vechain/sdk-network"; - -const privateKey = await Secp256k1.generatePrivateKey(); -const publicKey = Secp256k1.derivePublicKey(privateKey); -const address = Address.ofPublicKey(publicKey); - -const clauses: TransactionClause[] = [ - Clause.transferVET( - address, - VET.of(10000) - ) as TransactionClause -]; - -const transaction = { - clauses: clauses -}; - -const thorClient = ThorClient.fromUrl(THOR_SOLO_URL); - -const gasResult = await thorClient.gas.estimateGas( - transaction.clauses -); - -console.log(gasResult); - -const txBody = await thorClient.transactions.buildTransactionBody( - transaction.clauses, - gasResult.totalGas -); - -console.log(txBody); - - - -thorClient.destroy(); From 880e8699f05f31ffcce2d53d29d2e5e69c244031 Mon Sep 17 00:00:00 2001 From: lucanicoladebiasi Date: Thu, 24 Oct 2024 09:31:23 +0100 Subject: [PATCH 02/15] Financial math lesson... --- src/1.Hello_World/FinancialMath.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/1.Hello_World/FinancialMath.md b/src/1.Hello_World/FinancialMath.md index c8bed57..74ce9b3 100644 --- a/src/1.Hello_World/FinancialMath.md +++ b/src/1.Hello_World/FinancialMath.md @@ -3,7 +3,7 @@ # 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` type to compute math. +of rendering, initially just the `string` type to express texts and `number` and `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, @@ -11,7 +11,7 @@ the need to pinpoint the data type of a variable pushed the creation of TypeScri 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. -## Number Limits +## 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. @@ -70,6 +70,12 @@ The VeChain SDK provides the `FixedPointNumber` class to provide an unrestricted as `bigint` but supporting the concept of 'ratio' and rational numbers and 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 +on `bigint` hence the code developed using the SDK runs smoothly on any JS runtime because `bigint` type is +a base type of the JS language. + +In JS there are other libraries addressing the same problem to represent with adjustable precision + https://mikemcl.github.io/bignumber.js/ From 06f281fb67c6d50ed9c7625843178becb25b137b Mon Sep 17 00:00:00 2001 From: lucanicoladebiasi Date: Thu, 24 Oct 2024 11:34:49 +0100 Subject: [PATCH 03/15] Financial math lesson... --- src/1.Hello_World/FinancialMath.md | 61 +++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/src/1.Hello_World/FinancialMath.md b/src/1.Hello_World/FinancialMath.md index 74ce9b3..b3195dc 100644 --- a/src/1.Hello_World/FinancialMath.md +++ b/src/1.Hello_World/FinancialMath.md @@ -8,23 +8,26 @@ JS was not designed to be strongly typed language, but the language was designed 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 a variable pushed the creation of TypeScript (TS) -as a language derived by and compatible with JS making explicit the type definition of variables, +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. ## 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) +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. 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 210 = 1024 as exponent value, - 52 bits to represent an integer - named **significand** multiplied for the above **exponent** and **sign**, 252 is roughly equal to 4500000000000000 (45 followed by fourteen zeros). -Any value can't be represented exactly as a product of the integers **significand** * **2exponent** * **sign** +Any value can't be represented exactly as a product of the integers **significand** * **2exponent** * **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−308 to ±1.80×10308 in 16 +Actually, IEEE-754 represents precisely values in the range ±2.23×10−308 to ±1.80×10308 in 16 decimal digits comprehensive of the integer and fractional parts. It's important to anticipate due to the finite quantity of computer memory and the binary nature of electronic logic, @@ -74,8 +77,54 @@ The most important characteristic of the `FixedPointNumber` class is the algorit on `bigint` hence the code developed using the SDK runs smoothly on any JS runtime because `bigint` type is a base type of the JS language. -In JS there are other libraries addressing the same problem to represent with adjustable precision +### FixedPointNumber Compared With 'bignumber.js' Library + +In JS there are other libraries addressing the same problem to represent 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 +of the same methods provided by **bignumber.js**. Being **bignumber.js** a popular and excellent library +to deal with financial math in JS, `FixedPointNumber` class is designed to make trivial to adapt code +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`. + +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 × 104 is represented internally as + +- **significand** = `[1,2,3,4,5,6,7]` +- **exponent** = `4` +- **sign** = `1` (to flag a negative value). + +### FixedPointNumber Math + +The `FixedPointNumber` class adopts a much simpler implementation inspired to the way the blockchain protocols, +like Bitcoin, Ethereum and Thor represents cryptocurrencies values. + +- If the value is an integer, it's represented in a `bigint`. +- If the value is a rational value, hence it has a fractional part between ±1 and 0, it is scaled up multiplying itself + by 10 for the number of times equal to the number of digits required to write the fractional part. + +For this reason the `FixedPointNumber` exposes two properties, both of `bigint` type: +- `scaledValue` expresses the original value + - if integer the integer itself, + - if not integer the value multiplied by 10`fractionalDigits` +- `fractionalDigit` expresses the number of decimal digits required to write the fractional part of the value, it's `0` + for integers. -https://mikemcl.github.io/bignumber.js/ +For example 1.234567 is encoded as +- `scaledvalue = 1234567` (as 1.234567 × 106), +- `fractionalDigit = 6`. +The expression 'fixed point' number means a `FixedPointNumber` objects use `fractionalDigit` to fix its +precision and pinpoint where the decimal separator, (the `.` dot character by default) is placed when the value +is printed. From 62b0af9d21e9b5107699a8af4301f1523aeab35a Mon Sep 17 00:00:00 2001 From: lucanicoladebiasi Date: Thu, 24 Oct 2024 15:18:22 +0100 Subject: [PATCH 04/15] Financial math lesson... --- src/1.Hello_World/FinancialMath.md | 199 ++++++++++++++++++++++++++++- src/1.Hello_World/FinancialMath.ts | 79 +++++++++--- 2 files changed, 258 insertions(+), 20 deletions(-) diff --git a/src/1.Hello_World/FinancialMath.md b/src/1.Hello_World/FinancialMath.md index b3195dc..37c73f8 100644 --- a/src/1.Hello_World/FinancialMath.md +++ b/src/1.Hello_World/FinancialMath.md @@ -54,7 +54,7 @@ VeChain [VET](https://docs.vechain.org/introduction-to-vechain/dual-token-econom express the units, hence IEEE-754 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 sub-unit named **wei**, +cryptocurrency values as an integer value of its atomic smallest subunit named **wei**, to honor the computer engineer [Wei Dai](https://en.wikipedia.org/wiki/Wei_Dai). JS provides the @@ -87,7 +87,7 @@ of the same methods provided by **bignumber.js**. Being **bignumber.js** a popul to deal with financial math in JS, `FixedPointNumber` class is designed to make trivial to adapt code 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 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, @@ -122,9 +122,204 @@ For this reason the `FixedPointNumber` exposes two properties, both of `bigint` for integers. For example 1.234567 is encoded as + - `scaledvalue = 1234567` (as 1.234567 × 106), - `fractionalDigit = 6`. The expression 'fixed point' number means a `FixedPointNumber` objects use `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)` +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 +objects and how the `.bi` and `.n` properties behave. + +```typescript +// STEP 1: import FixedPointNumber and BigNumber to compare results among the two libraries. +import {FixedPointNumber} from '@vechain/sdk-core'; +import {BigNumber} from 'bignumber.js'; + +// STEP 2: create two equivalent objects from different expression types and check they are equivalent. +let x = FixedPointNumber.of(123.456789); +let y = FixedPointNumber.of('123.456789'); +console.log(`FPN value ${x} from number is ${x.isEqual(y) ? '' : 'not'}equal to ${y} from string.`); + +// STEP 3: cast to a number +console.log(`Cast FPN value to number is ${x.n}.`); + +// STEP 4: cast a rational value to a bigint type truncates the value to integer. +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. + +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 1020 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. + +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. + +For the 1/3 ratio the division is made between +10000000000000000000000000000000000000000/30000000000000000000000000000000000000000 +hence the integer scaled up by 10default 20 fractional digit precision multiplied 2 times: +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 +to the less meaningful digit. As recalled many times, computers must approximate at some point. +Therm, the 40 digits internal precision is scaled down 10default 20 fractional digit 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 +assure at least the wished precision, consuming more memory only during the computation, returning to the +operating system after the result is computed, in any case consuming less memory than **bignumber.js** +implementation, the latter uses 8 bytes per each digit of precision. + +The following example divide 1 by 3 comparing three results made by JS numbers, **bignumber.js** math and +`FixedPointNumber` math with the default precision of 20 fractional digits. + +```typescript +// STEP 5: compute 1/3 comparing JS number, BigNumber and SDK FixedPointNumber math +x = FixedPointNumber.of(1); +y = FixedPointNumber.of(3); +let r = x.div(y); // r for 'ratio'. +console.log(`${x}/${y} => JS = ${x.n / y.n};\tBigNumber = ${BigNumber(x.n).div(y.n)};\tSDK = ${r}`); +``` + +For sake of comprehension, the snipped above prints + +```text +1/3 => JS = 0.33333333333333326; BigNumber = 0.33333333333333328889; SDK = 0.33333333333333333333 +``` + +As expected JS `number` type math is precise in 16 digits included the integer `0` and the following fifteen `3`, then +diverge. + +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 +precision, set `.dp(25)` and discard the last five digits. + +The SDK math returns the value closer to the theoretical real number ratio. + +Let's see what happen when we increase the precision to 80 fractional digits. + +```typescript +// STEP 6: increase the precision to 80 decimal digits. +let fd = 80; // Fractional Digits. +x = x.dp(80); // Force x to fd precision, .div function will adapt y automatically +r = x.div(y); // Ratio +let a = BigNumber(x.n).dp(fd); // Force to fd precision. +let b = BigNumber(y.n).dp(fd); // Force to fd precision +let q = a.div(b) // q for 'quotient' synonymous of 'ratio'. +console.log(`${x}/${y} => BigNumber ${q};\t SDK = ${r}`); +``` + +The code prints + +```text +1/3 => BigNumber 0.33333333333333328889; SDK = 0.33333333333333333333333333333333333333333333333333333333333333333333333333333333 +``` + +because **bignumber.js** approximation algorithm converge to 0.33333333333333328889 followed by not meaningful zeros. +The `FixedPointNumber` is more accurate regarding divisions. + +The next example computes the square root on few natural numbers, let's see how JS, **bignumber.js** and +`FixedPointNumber` class behave. + +```typescript +// STEP 7: compute the squared root of the natural number from 0 to n. +fd = 20; +let n = 8 +let rows = []; +for (let i = 0; i <= n; i++) { + x = FixedPointNumber.of(i).dp(fd).sqrt(); + a = BigNumber(i).dp(fd).sqrt(); + rows.push({ + 'JS': Math.sqrt(i), + 'bignumber.js': `${a}`, + 'SDK FixedPointNumber': `${x}` + }) +} +console.table(rows); +``` + +The code prints the table + +```text +┌─────────┬────────────────────┬──────────────────────────┬──────────────────────────┐ +│ (index) │ JS │ bignumber.js │ SDK FixedPointNumber │ +├─────────┼────────────────────┼──────────────────────────┼──────────────────────────┤ +│ 0 │ 0 │ '0' │ '0' │ +│ 1 │ 1 │ '1' │ '1' │ +│ 2 │ 1.4142135623730951 │ '1.4142135623730950488' │ '1.4142135623730950488' │ +│ 3 │ 1.7320508075688772 │ '1.73205080756887729353' │ '1.73205080756887729352' │ +│ 4 │ 2 │ '2' │ '2' │ +│ 5 │ 2.23606797749979 │ '2.23606797749978969641' │ '2.2360679774997896964' │ +│ 6 │ 2.449489742783178 │ '2.4494897427831780982' │ '2.44948974278317809819' │ +│ 7 │ 2.6457513110645907 │ '2.6457513110645905905' │ '2.6457513110645905905' │ +│ 8 │ 2.8284271247461903 │ '2.8284271247461900976' │ '2.8284271247461900976' │ +└─────────┴────────────────────┴──────────────────────────┴──────────────────────────┘ +``` + +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 +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 +th +digit internally resolving the computation with 160 digits. + +The code + +```typescript +// STEP 8: compute the squared root of the natural number from 0 to n. +fd = 80; +n = 8 +rows = []; +for (let i = 0; i <= n; i++) { + x = FixedPointNumber.of(i).dp(fd).sqrt(); + a = BigNumber(i).dp(fd).sqrt(); + rows.push({ + 'JS': Math.sqrt(i), + 'bignumber.js': `${a}`, + 'SDK FixedPointNumber': `${x}` + }) +} +console.table(rows); +``` + +prints + +```text +┌─────────┬────────────────────┬──────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────┐ +│ (index) │ JS │ bignumber.js │ SDK FixedPointNumber │ +├─────────┼────────────────────┼──────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────┤ +│ 0 │ 0 │ '0' │ '0' │ +│ 1 │ 1 │ '1' │ '1' │ +│ 2 │ 1.4142135623730951 │ '1.4142135623730950488' │ '1.41421356237309504880168872420969807856967187537694807317667973799073247846210703' │ +│ 3 │ 1.7320508075688772 │ '1.73205080756887729353' │ '1.73205080756887729352744634150587236694280525381038062805580697945193301690880003' │ +│ 4 │ 2 │ '2' │ '2' │ +│ 5 │ 2.23606797749979 │ '2.23606797749978969641' │ '2.23606797749978969640917366873127623544061835961152572427089724541052092563780489' │ +│ 6 │ 2.449489742783178 │ '2.4494897427831780982' │ '2.44948974278317809819728407470589139196594748065667012843269256725096037745731502' │ +│ 7 │ 2.6457513110645907 │ '2.6457513110645905905' │ '2.64575131106459059050161575363926042571025918308245018036833445920106882323028362' │ +│ 8 │ 2.8284271247461903 │ '2.8284271247461900976' │ '2.82842712474619009760337744841939615713934375075389614635335947598146495692421407' │ +└─────────┴────────────────────┴──────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────┘ +``` + +Note if you manually round the long SDK results to the digints printed in the **bignumber.js** result, you will get the +**bignumber.js** results. +The two libraries approximate the theoretical real number results consistently but `FixedPointNumber` works +with higher precision when instructed to do because it was designed to respect at least the 20 digits of fractional +precision cryptocurrency math requires. + diff --git a/src/1.Hello_World/FinancialMath.ts b/src/1.Hello_World/FinancialMath.ts index aa11199..427ff45 100644 --- a/src/1.Hello_World/FinancialMath.ts +++ b/src/1.Hello_World/FinancialMath.ts @@ -1,31 +1,74 @@ +// START-SNIPPET: FinancialMath_1 +// STEP 1: import FixedPointNumber and BigNumber to compare results among the two libraries. import {FixedPointNumber} from '@vechain/sdk-core'; -import { BigNumber } from 'bignumber.js'; +import {BigNumber} from 'bignumber.js'; +// STEP 2: create two equivalent objects from different expression types and check they are equivalent. let x = FixedPointNumber.of(123.456789); let y = FixedPointNumber.of('123.456789'); console.log(`FPN value ${x} from number is ${x.isEqual(y) ? '' : 'not'}equal to ${y} from string.`); + +// STEP 3: cast to a number console.log(`Cast FPN value to number is ${x.n}.`); + +// STEP 4: cast a rational value to a bigint type truncates the value to integer. console.log(`Cast FPN value to bigint is ${x.bi}.`); +// END-SNIPPET: FinancialMath_1 + +// START-SNIPPET: FinancialMath_2 +// STEP 5: compute 1/3 comparing JS number, BigNumber and SDK FixedPointNumber math x = FixedPointNumber.of(1); y = FixedPointNumber.of(3); -let r = x.div(y); -console.log(`FPN value = ${r}; JS value = ${x.n/y.n}; BigNumber value = ${BigNumber(x.n).div(y.n)}.`); -x = x.dp(80) // must be updated -r = x.div(y); -console.log(`${r}`); - -let dp = 20; -for(let n = 0; n <= 8; n ++) { - x = FixedPointNumber.of(n, BigInt(dp)); - r = x.sqrt(); - console.log(`${n}, ${r};\t${Math.sqrt(n)};\t${BigNumber(n).dp(dp).sqrt()}`); +let r = x.div(y); // r for 'ratio'. +console.log(`${x}/${y} => JS = ${x.n / y.n};\tBigNumber = ${BigNumber(x.n).div(y.n)};\tSDK = ${r}`); + +// END-SNIPPET: FinancialMath_2 + +// START-SNIPPET: FinancialMath_3 +// STEP 6: increase the precision to 80 decimal digits. +let fd = 80; // Fractional Digits. +x = x.dp(80); // Force x to fd precision, .div function will adapt y automatically +r = x.div(y); // Ratio +let a = BigNumber(x.n).dp(fd); // Force to fd precision. +let b = BigNumber(y.n).dp(fd); // Force to fd precision +let q = a.div(b) // q for 'quotient' synonymous of 'ratio'. +console.log(`${x}/${y} => BigNumber ${q};\t SDK = ${r}`); + +// END-SNIPPET: FinancialMath_3 + +// START-SNIPPET: FinancialMath_3 +// STEP 7: compute the squared root of the natural number from 0 to n. +fd = 20; +let n = 8 +let rows = []; +for (let i = 0; i <= n; i++) { + x = FixedPointNumber.of(i).dp(fd).sqrt(); + a = BigNumber(i).dp(fd).sqrt(); + rows.push({ + 'JS': Math.sqrt(i), + 'bignumber.js': `${a}`, + 'SDK FixedPointNumber': `${x}` + }) } +console.table(rows); -// let dp = 20; -// for(let n = 0; n <= 8; n ++) { -// x = FixedPointNumber.of(n, BigInt(dp)); -// r = x.sqrt(); -// console.log(`${n}, ${r};\t${Math.sqrt(n)};\t${BigNumber(n).dp(dp).sqrt()}`); -// } +// END-SNIPPET: FinancialMath_3 + +// START-SNIPPET: FinancialMath_4 +// STEP 8: compute the squared root of the natural number from 0 to n. +fd = 80; +n = 8 +rows = []; +for (let i = 0; i <= n; i++) { + x = FixedPointNumber.of(i).dp(fd).sqrt(); + a = BigNumber(i).dp(fd).sqrt(); + rows.push({ + 'JS': Math.sqrt(i), + 'bignumber.js': `${a}`, + 'SDK FixedPointNumber': `${x}` + }) +} +console.table(rows); +// END-SNIPPET: FinancialMath_3 From 7c30573df29a9265009b2d3929269a826ca9dcf6 Mon Sep 17 00:00:00 2001 From: lucanicoladebiasi Date: Thu, 24 Oct 2024 16:27:16 +0100 Subject: [PATCH 05/15] Financial math lesson... --- src/1.Hello_World/FinancialMath.md | 41 +++++++++++++++++++++++++----- src/1.Hello_World/FinancialMath.ts | 17 ++++++++++--- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/1.Hello_World/FinancialMath.md b/src/1.Hello_World/FinancialMath.md index 37c73f8..c300606 100644 --- a/src/1.Hello_World/FinancialMath.md +++ b/src/1.Hello_World/FinancialMath.md @@ -7,7 +7,7 @@ of rendering, initially just the `string` type to express texts and `number` and 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 a variable pushed the creation of TypeScript (TS) +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. @@ -155,6 +155,8 @@ console.log(`Cast FPN value to number is ${x.n}.`); console.log(`Cast FPN value to bigint is ${x.bi}.`); ``` +### Division Challenge + 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. @@ -234,6 +236,8 @@ The code prints because **bignumber.js** approximation algorithm converge to 0.33333333333333328889 followed by not meaningful zeros. 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 `FixedPointNumber` class behave. @@ -247,7 +251,7 @@ for (let i = 0; i <= n; i++) { a = BigNumber(i).dp(fd).sqrt(); rows.push({ 'JS': Math.sqrt(i), - 'bignumber.js': `${a}`, + 'BigNumber': `${a}`, 'SDK FixedPointNumber': `${x}` }) } @@ -258,7 +262,7 @@ The code prints the table ```text ┌─────────┬────────────────────┬──────────────────────────┬──────────────────────────┐ -│ (index) │ JS │ bignumber.js │ SDK FixedPointNumber │ +│ (index) │ JS │ BigNumber │ SDK FixedPointNumber │ ├─────────┼────────────────────┼──────────────────────────┼──────────────────────────┤ │ 0 │ 0 │ '0' │ '0' │ │ 1 │ 1 │ '1' │ '1' │ @@ -292,7 +296,7 @@ for (let i = 0; i <= n; i++) { a = BigNumber(i).dp(fd).sqrt(); rows.push({ 'JS': Math.sqrt(i), - 'bignumber.js': `${a}`, + 'BigNumber': `${a}`, 'SDK FixedPointNumber': `${x}` }) } @@ -303,7 +307,7 @@ prints ```text ┌─────────┬────────────────────┬──────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────┐ -│ (index) │ JS │ bignumber.js │ SDK FixedPointNumber │ +│ (index) │ JS │ BigNumber │ SDK FixedPointNumber │ ├─────────┼────────────────────┼──────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────┤ │ 0 │ 0 │ '0' │ '0' │ │ 1 │ 1 │ '1' │ '1' │ @@ -317,9 +321,34 @@ prints └─────────┴────────────────────┴──────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────┘ ``` -Note if you manually round the long SDK results to the digints printed in the **bignumber.js** result, you will get the +Note if you manually round the long SDK results to the digits printed in the **bignumber.js** result, you will get the **bignumber.js** results. The two libraries approximate the theoretical real number results consistently but `FixedPointNumber` works with higher precision when instructed to do because it was designed to respect at least the 20 digits of fractional precision cryptocurrency math requires. +### Limit Challenge + +The `FixedPointNumber` class works as the JS `number` does to approximate the results at the limits. +the following examples computes the most classic limits of the division, by zero, by infinity and between zero arguments. + +```text +// START-SNIPPET: FinancialMath_5 +// STEP 9: compute the divisions by zero, infinity and between zeros. +r = x.div(FixedPointNumber.ZERO); +console.log(`${x}/0 = ${r}`); +r = x.div(FixedPointNumber.of(Infinity)); +console.log(`${x}/infinity = ${r}`); +r = x.div(FixedPointNumber.ZERO.div(FixedPointNumber.ZERO)); +console.log(`0/0 = is ${r}`); +``` + +printing, whatever is the last value of `x` + +```text +2.82842712474619009760337744841939615713934375075389614635335947598146495692421407/0 = Infinity +2.82842712474619009760337744841939615713934375075389614635335947598146495692421407/infinity = 0 +0/0 = is NaN +``` + +as expected. diff --git a/src/1.Hello_World/FinancialMath.ts b/src/1.Hello_World/FinancialMath.ts index 427ff45..98656d6 100644 --- a/src/1.Hello_World/FinancialMath.ts +++ b/src/1.Hello_World/FinancialMath.ts @@ -47,7 +47,7 @@ for (let i = 0; i <= n; i++) { a = BigNumber(i).dp(fd).sqrt(); rows.push({ 'JS': Math.sqrt(i), - 'bignumber.js': `${a}`, + 'BigNumber': `${a}`, 'SDK FixedPointNumber': `${x}` }) } @@ -65,10 +65,21 @@ for (let i = 0; i <= n; i++) { a = BigNumber(i).dp(fd).sqrt(); rows.push({ 'JS': Math.sqrt(i), - 'bignumber.js': `${a}`, + 'BigNumber': `${a}`, 'SDK FixedPointNumber': `${x}` }) } console.table(rows); -// END-SNIPPET: FinancialMath_3 +// END-SNIPPET: FinancialMath_4 + +// START-SNIPPET: FinancialMath_5 +// STEP 9: compute the divisions by zero, infinity and between zeros. +r = x.div(FixedPointNumber.ZERO); +console.log(`${x}/0 = ${r}`); +r = x.div(FixedPointNumber.of(Infinity)); +console.log(`${x}/infinity = ${r}`); +r = x.div(FixedPointNumber.ZERO.div(FixedPointNumber.ZERO)); +console.log(`0/0 = is ${r}`); + +// END-SNIPPET: FinancialMath_5 From 5f4389a7e5dff6fd7c9883360106d48d87d9f7ac Mon Sep 17 00:00:00 2001 From: lucanicoladebiasi Date: Thu, 24 Oct 2024 16:29:48 +0100 Subject: [PATCH 06/15] Financial math lesson... --- src/1.Hello_World/FinancialMath.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/1.Hello_World/FinancialMath.md b/src/1.Hello_World/FinancialMath.md index c300606..bb18622 100644 --- a/src/1.Hello_World/FinancialMath.md +++ b/src/1.Hello_World/FinancialMath.md @@ -330,9 +330,9 @@ precision cryptocurrency math requires. ### Limit Challenge The `FixedPointNumber` class works as the JS `number` does to approximate the results at the limits. -the following examples computes the most classic limits of the division, by zero, by infinity and between zero arguments. +The following examples computes the most classic limits of the division, by zero, by infinity and between zero arguments. -```text +```typescript // START-SNIPPET: FinancialMath_5 // STEP 9: compute the divisions by zero, infinity and between zeros. r = x.div(FixedPointNumber.ZERO); @@ -343,7 +343,7 @@ r = x.div(FixedPointNumber.ZERO.div(FixedPointNumber.ZERO)); console.log(`0/0 = is ${r}`); ``` -printing, whatever is the last value of `x` +Whatever is the last value of `x`, the code prints ```text 2.82842712474619009760337744841939615713934375075389614635335947598146495692421407/0 = Infinity From 4cf7c262e3ddc377170493a4b0bb1662840164ec Mon Sep 17 00:00:00 2001 From: lucanicoladebiasi Date: Thu, 24 Oct 2024 17:41:47 +0100 Subject: [PATCH 07/15] Financial math lesson... --- src/1.Hello_World/FinancialMath.md | 33 ++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/1.Hello_World/FinancialMath.md b/src/1.Hello_World/FinancialMath.md index bb18622..cef40a6 100644 --- a/src/1.Hello_World/FinancialMath.md +++ b/src/1.Hello_World/FinancialMath.md @@ -30,13 +30,14 @@ is approximated with less precision for values very big or very small fraction, Actually, IEEE-754 represents precisely values in the range ±2.23×10−308 to ±1.80×10308 in 16 decimal digits comprehensive of the integer and fractional parts. -It's important to anticipate 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 power 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') +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 π 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 @@ -77,7 +78,7 @@ The most important characteristic of the `FixedPointNumber` class is the algorit on `bigint` hence the code developed using the SDK runs smoothly on any JS runtime because `bigint` type is a base type of the JS language. -### FixedPointNumber Compared With 'bignumber.js' Library +### bignumber.js comparison In JS there are other libraries addressing the same problem to represent with adjustable precision. Among them, it is worth to name [**bignumber.js**](https://mikemcl.github.io/bignumber.js/). @@ -104,7 +105,16 @@ for example the value -1.234567 × 104 is represented internally as - **exponent** = `4` - **sign** = `1` (to flag a negative value). -### FixedPointNumber Math +The mention and comparison with **bignumber.js** is due because `FixedPointNumber` class doesn't implement +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 (n0.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. + + +### FixedPointNumber math The `FixedPointNumber` class adopts a much simpler implementation inspired to the way the blockchain protocols, like Bitcoin, Ethereum and Thor represents cryptocurrencies values. @@ -155,7 +165,7 @@ console.log(`Cast FPN value to number is ${x.n}.`); console.log(`Cast FPN value to bigint is ${x.bi}.`); ``` -### Division Challenge +### Division challenge 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 @@ -236,7 +246,7 @@ The code prints because **bignumber.js** approximation algorithm converge to 0.33333333333333328889 followed by not meaningful zeros. The `FixedPointNumber` is more accurate regarding divisions. -### Square Root Challenge +### Square root challenge The next example computes the square root on few natural numbers, let's see how JS, **bignumber.js** and `FixedPointNumber` class behave. @@ -327,10 +337,11 @@ The two libraries approximate the theoretical real number results consistently b with higher precision when instructed to do because it was designed to respect at least the 20 digits of fractional precision cryptocurrency math requires. -### Limit Challenge +### Limit challenge The `FixedPointNumber` class works as the JS `number` does to approximate the results at the limits. -The following examples computes the most classic limits of the division, by zero, by infinity and between zero arguments. +The following examples computes the most classic limits of the division, by zero, by infinity and between zero +arguments. ```typescript // START-SNIPPET: FinancialMath_5 From 0ce919fe11e7abcd4aec6e8c67c3b3f04032b257 Mon Sep 17 00:00:00 2001 From: lucanicoladebiasi Date: Thu, 24 Oct 2024 17:48:39 +0100 Subject: [PATCH 08/15] Financial math lesson... --- src/1.Hello_World/FinancialMath.md | 4 ++-- src/1.Hello_World/FinancialMath.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/1.Hello_World/FinancialMath.md b/src/1.Hello_World/FinancialMath.md index cef40a6..34daa49 100644 --- a/src/1.Hello_World/FinancialMath.md +++ b/src/1.Hello_World/FinancialMath.md @@ -351,7 +351,7 @@ console.log(`${x}/0 = ${r}`); r = x.div(FixedPointNumber.of(Infinity)); console.log(`${x}/infinity = ${r}`); r = x.div(FixedPointNumber.ZERO.div(FixedPointNumber.ZERO)); -console.log(`0/0 = is ${r}`); +console.log(`0/0 = ${r}`); ``` Whatever is the last value of `x`, the code prints @@ -359,7 +359,7 @@ Whatever is the last value of `x`, the code prints ```text 2.82842712474619009760337744841939615713934375075389614635335947598146495692421407/0 = Infinity 2.82842712474619009760337744841939615713934375075389614635335947598146495692421407/infinity = 0 -0/0 = is NaN +0/0 = NaN ``` as expected. diff --git a/src/1.Hello_World/FinancialMath.ts b/src/1.Hello_World/FinancialMath.ts index 98656d6..9d41cc1 100644 --- a/src/1.Hello_World/FinancialMath.ts +++ b/src/1.Hello_World/FinancialMath.ts @@ -80,6 +80,6 @@ console.log(`${x}/0 = ${r}`); r = x.div(FixedPointNumber.of(Infinity)); console.log(`${x}/infinity = ${r}`); r = x.div(FixedPointNumber.ZERO.div(FixedPointNumber.ZERO)); -console.log(`0/0 = is ${r}`); +console.log(`0/0 = ${r}`); // END-SNIPPET: FinancialMath_5 From 9e06170ee5055fd2db2112e18b50973e7bebe653 Mon Sep 17 00:00:00 2001 From: lucanicoladebiasi Date: Thu, 24 Oct 2024 17:53:16 +0100 Subject: [PATCH 09/15] Financial math lesson... --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c7ea6c..fc284ee 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ The [src](src) directory provides a list of lessons with exercises and solutions 1. * [Hello World!](src/1.Hello_World/HelloWorld.md) + * [Financial Math: `FixedPointNumber` class.](src/1.Hello_World/FinancialMath.md) 2. * [The Bloom filter.](src/2.Bloom_Filter/BloomPart1.md) * [The Bloom filter: advanced topics on `k` and `m` parameters.](src/2.Bloom_Filter/BloomPart2.md) @@ -27,4 +28,4 @@ This project is licensed under the [MIT license](LICENSE.md). ## Contact information - Discord https://discord.com/invite/vechain -- Support https://support.vechain.org \ No newline at end of file +- Support https://support.vechain.org From 2a9a9b8d22a7f033e9446dbb89ec64afb83125b6 Mon Sep 17 00:00:00 2001 From: lucanicoladebiasi Date: Thu, 24 Oct 2024 17:53:16 +0100 Subject: [PATCH 10/15] Financial math lesson. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c7ea6c..fc284ee 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ The [src](src) directory provides a list of lessons with exercises and solutions 1. * [Hello World!](src/1.Hello_World/HelloWorld.md) + * [Financial Math: `FixedPointNumber` class.](src/1.Hello_World/FinancialMath.md) 2. * [The Bloom filter.](src/2.Bloom_Filter/BloomPart1.md) * [The Bloom filter: advanced topics on `k` and `m` parameters.](src/2.Bloom_Filter/BloomPart2.md) @@ -27,4 +28,4 @@ This project is licensed under the [MIT license](LICENSE.md). ## Contact information - Discord https://discord.com/invite/vechain -- Support https://support.vechain.org \ No newline at end of file +- Support https://support.vechain.org From 5278e92dd9fd31dbddeb97745a029ccff494ad0c Mon Sep 17 00:00:00 2001 From: lucanicoladebiasi Date: Thu, 24 Oct 2024 20:27:16 +0100 Subject: [PATCH 11/15] Financial math lesson. --- src/1.Hello_World/FinancialMath.md | 8 ++++---- src/1.Hello_World/FinancialMath.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/1.Hello_World/FinancialMath.md b/src/1.Hello_World/FinancialMath.md index 34daa49..3a1ec59 100644 --- a/src/1.Hello_World/FinancialMath.md +++ b/src/1.Hello_World/FinancialMath.md @@ -260,7 +260,7 @@ for (let i = 0; i <= n; i++) { x = FixedPointNumber.of(i).dp(fd).sqrt(); a = BigNumber(i).dp(fd).sqrt(); rows.push({ - 'JS': Math.sqrt(i), + 'JS number': Math.sqrt(i), 'BigNumber': `${a}`, 'SDK FixedPointNumber': `${x}` }) @@ -272,7 +272,7 @@ The code prints the table ```text ┌─────────┬────────────────────┬──────────────────────────┬──────────────────────────┐ -│ (index) │ JS │ BigNumber │ SDK FixedPointNumber │ +│ (index) │ JS number │ BigNumber │ SDK FixedPointNumber │ ├─────────┼────────────────────┼──────────────────────────┼──────────────────────────┤ │ 0 │ 0 │ '0' │ '0' │ │ 1 │ 1 │ '1' │ '1' │ @@ -305,7 +305,7 @@ for (let i = 0; i <= n; i++) { x = FixedPointNumber.of(i).dp(fd).sqrt(); a = BigNumber(i).dp(fd).sqrt(); rows.push({ - 'JS': Math.sqrt(i), + 'JS number': Math.sqrt(i), 'BigNumber': `${a}`, 'SDK FixedPointNumber': `${x}` }) @@ -317,7 +317,7 @@ prints ```text ┌─────────┬────────────────────┬──────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────┐ -│ (index) │ JS │ BigNumber │ SDK FixedPointNumber │ +│ (index) │ JS number │ BigNumber │ SDK FixedPointNumber │ ├─────────┼────────────────────┼──────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────┤ │ 0 │ 0 │ '0' │ '0' │ │ 1 │ 1 │ '1' │ '1' │ diff --git a/src/1.Hello_World/FinancialMath.ts b/src/1.Hello_World/FinancialMath.ts index 9d41cc1..f61a43a 100644 --- a/src/1.Hello_World/FinancialMath.ts +++ b/src/1.Hello_World/FinancialMath.ts @@ -46,7 +46,7 @@ for (let i = 0; i <= n; i++) { x = FixedPointNumber.of(i).dp(fd).sqrt(); a = BigNumber(i).dp(fd).sqrt(); rows.push({ - 'JS': Math.sqrt(i), + 'JS number': Math.sqrt(i), 'BigNumber': `${a}`, 'SDK FixedPointNumber': `${x}` }) @@ -64,7 +64,7 @@ for (let i = 0; i <= n; i++) { x = FixedPointNumber.of(i).dp(fd).sqrt(); a = BigNumber(i).dp(fd).sqrt(); rows.push({ - 'JS': Math.sqrt(i), + 'JS number': Math.sqrt(i), 'BigNumber': `${a}`, 'SDK FixedPointNumber': `${x}` }) From 29be336a02d0a6f5e015c9db3d080aaf2011981b Mon Sep 17 00:00:00 2001 From: lucanicoladebiasi Date: Thu, 24 Oct 2024 20:34:57 +0100 Subject: [PATCH 12/15] Financial math lesson. --- src/1.Hello_World/FinancialMath.md | 4 ++-- src/1.Hello_World/FinancialMath.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/1.Hello_World/FinancialMath.md b/src/1.Hello_World/FinancialMath.md index 3a1ec59..0484607 100644 --- a/src/1.Hello_World/FinancialMath.md +++ b/src/1.Hello_World/FinancialMath.md @@ -349,7 +349,7 @@ arguments. r = x.div(FixedPointNumber.ZERO); console.log(`${x}/0 = ${r}`); r = x.div(FixedPointNumber.of(Infinity)); -console.log(`${x}/infinity = ${r}`); +console.log(`${x}/Infinity = ${r}`); r = x.div(FixedPointNumber.ZERO.div(FixedPointNumber.ZERO)); console.log(`0/0 = ${r}`); ``` @@ -358,7 +358,7 @@ Whatever is the last value of `x`, the code prints ```text 2.82842712474619009760337744841939615713934375075389614635335947598146495692421407/0 = Infinity -2.82842712474619009760337744841939615713934375075389614635335947598146495692421407/infinity = 0 +2.82842712474619009760337744841939615713934375075389614635335947598146495692421407/Infinity = 0 0/0 = NaN ``` diff --git a/src/1.Hello_World/FinancialMath.ts b/src/1.Hello_World/FinancialMath.ts index f61a43a..674ecbf 100644 --- a/src/1.Hello_World/FinancialMath.ts +++ b/src/1.Hello_World/FinancialMath.ts @@ -78,7 +78,7 @@ console.table(rows); r = x.div(FixedPointNumber.ZERO); console.log(`${x}/0 = ${r}`); r = x.div(FixedPointNumber.of(Infinity)); -console.log(`${x}/infinity = ${r}`); +console.log(`${x}/Infinity = ${r}`); r = x.div(FixedPointNumber.ZERO.div(FixedPointNumber.ZERO)); console.log(`0/0 = ${r}`); From 53785a62b08f06b01a116c9028e98a3bd0d51445 Mon Sep 17 00:00:00 2001 From: lucanicoladebiasi Date: Thu, 24 Oct 2024 21:20:46 +0100 Subject: [PATCH 13/15] Financial math lesson. --- src/1.Hello_World/FinancialMath.md | 114 ++++++++++++++--------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/src/1.Hello_World/FinancialMath.md b/src/1.Hello_World/FinancialMath.md index 0484607..72a1fae 100644 --- a/src/1.Hello_World/FinancialMath.md +++ b/src/1.Hello_World/FinancialMath.md @@ -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 210 = 1024 as exponent value, +- 11 bits to represent an **exponent**, one bit for the exponent sign and 210 = 0-1024 as exponent range + value, - 52 bits to represent an integer - named **significand** multiplied for the above **exponent** and **sign**, 252 is roughly equal to 4500000000000000 (45 followed by fourteen zeros). -Any value can't be represented exactly as a product of the integers **significand** * **2exponent** * **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** × **2exponent** +× **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−308 to ±1.80×10308 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 1014 hence at most 10 trillions (1012) +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 1014 hence at most 10 trillions (1012) 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**, @@ -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 @@ -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 @@ -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 × 104 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 × 104 is represented internally as - **significand** = `[1,2,3,4,5,6,7]` - **exponent** = `4` @@ -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 (n0.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 @@ -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`fractionalDigits` + - if not integer the value multiplied by 10`fractionalDigits`; - `fractionalDigit` expresses the number of decimal digits required to write the fractional part of the value, it's `0` for integers. @@ -136,16 +134,16 @@ For example 1.234567 is encoded as - `scaledvalue = 1234567` (as 1.234567 × 106), - `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 @@ -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 1020 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 1020 +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 10default 20 fractional digit precision multiplied 2 times: +10000000000000000000000000000000000000000 / 30000000000000000000000000000000000000000, +hence the integer is scaled up by 10default 20 fractional digit precision multiplied 2 times: 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 10default 20 fractional digit preserving +Then, the 40 digits internal precision is scaled down 10default 20 fractional digit 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 @@ -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. @@ -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 @@ -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 -th -digit internally resolving the computation with 160 digits. +th digit, internally resolving the computation with 160 digits. The code From 0ca74fcd3bf32caba1d21edfa63fea34732417a7 Mon Sep 17 00:00:00 2001 From: lucanicoladebiasi Date: Fri, 25 Oct 2024 16:32:43 +0100 Subject: [PATCH 14/15] Financial math lesson. --- src/1.Hello_World/FinancialMath.md | 138 +++++++++++++++++++++++++++++ src/1.Hello_World/FinancialMath.ts | 64 +++++++++++++ 2 files changed, 202 insertions(+) diff --git a/src/1.Hello_World/FinancialMath.md b/src/1.Hello_World/FinancialMath.md index 72a1fae..0271ae5 100644 --- a/src/1.Hello_World/FinancialMath.md +++ b/src/1.Hello_World/FinancialMath.md @@ -363,3 +363,141 @@ Whatever is the last value of `x`, the code prints ``` as expected. + +### Compound Interest challenge + +The [Compount Interest](https://en.wikipedia.org/wiki/Compound_interest) +is the result of reinvesting or retaining interest that would otherwise be paid out from a capital, +or of the accumulation of debts from a borrower. + +From the referred literature the formula to compute the + +- *A*: accrued amount, from the +- *P* := principal capital, at the +- *r* := [nominal annual interest rate](https://en.wikipedia.org/wiki/Nominal_interest_rate), when accrued +- *n* := times per +- *t* := number of years in decimal form, hence 6 month = 0.5 + +is the following expression. + +$$A=P(1+{r \over n})^{nt}$$ + +The following snipped shows how to implement the above formula with the `FixedPointNumber` class +for the simpler case of 10,000 currency units invested at 15% of interest rate annually. + +```typescript +// STEP 10: compute the simple interest reat for 10,000 currency unit at 15% for one year +let P = 10000; // 10,000 $ +let R = 0.15; // 15% interest rate +let N = 1; // interest accrued times per year +let T = 1; // 1 year of investment time +let jsA = interestWithNumberType(P, R, N, T); +console.log( + `JS number => ${P} at ${R} accrued ${N} per year for ${T} years = ${jsA} ` +); +let bnA = interestWithBigNumberType(P, R, N, T); +console.log( + `BigNumber => ${P} at ${R} accrued ${N} per year for ${T} years = ${bnA} ` +); +let fpA = interestWithFixedPointNumberType(P, R, N, T); +console.log( + `SDK FixedPointNumber => ${P} at ${R} accrued ${N} per year for ${T} years = ${fpA} ` +); + +``` + +The above code prints + +```text +JS number => 10000 at 0.15 accrued 1 per year for 1 years = 11500 +BigNumber => 10000 at 0.15 accrued 1 per year for 1 years = 11500 +SDK FixedPointNumber => 10000 at 0.15 accrued 1 per year for 1 years = 11500 +``` + +where the math is simple. + +It becomes interesting to understand if we could have accrued in the capital the interests once per day, +the code becomes + +```typescript +P = 10000; // 10,000 $ +R = 0.15; // 15% interest rate +N = 365; // interest accrued times per day +T = 1; // 1 year of investment time +jsA = interestWithNumberType(P, R, N, T); +console.log( + `JS number => ${P} at ${R} accrued ${N} per year for ${T} years = ${jsA} ` +); +bnA = interestWithBigNumberType(P, R, N, T); +console.log( + `BigNumber => ${P} at ${R} accrued ${N} per year for ${T} years = ${bnA} ` +); +fpA = interestWithFixedPointNumberType(P, R, N, T); +console.log( + `SDK FixedPointNumber => ${P} at ${R} accrued ${N} per year for ${T} years = ${fpA} ` +); +``` + +that prints the following output. + +```text +JS number => 10000 at 0.15 accrued 365 per year for 1 years = 11617.984431282737 +BigNumber => 10000 at 0.15 accrued 365 per year for 1 years = 11617.984431282297580741332627901777130315693092582736415183243388318237491778342908385720809654240040970598789674377765947470867911564401090655498297277650975577399724748298427632216300469085882205126498872953842419903091295485650124501686949728680155745247474335138232482179528909255366536402392875329941467045245858869644197239128719066296683081367462543205390000804087922075544539860855742520269818193809495203557007634259996219745309932882459518798524968113901703633929888998635141912068168058452472183416934462187935057393732956042640561423418567082114578032214061928405512072912635929995221365892079381592086022940837672235708964650239008816227116838590877049591322964899152767454901676441240192497489253888772433181792034083619461274791847315352488935494000067256440171197925364612917886966149610377974675067875969329030185742031433985309342124192478167223066139006719604689359394196296969241000167807707526911942624844129728153376077127099662124501925926262400807908100667459068640705163071829628876166416144650604075464967358748105805082716622225378985664759817456721566584649980686571966739424426900555857787691088510512370790518609850265247504195307410003670920350652938085871229040577564845370642100134126774852650664481607739716900248270176440329598644103824418655594316501156008480980716682782242614278880139844472436437042938201287303745900970102452173445939781488072924165863076960481844351048526193390029595200330434638261284649942836277464922095624061067458197847435464948760326222404388856873196780809435008547776084396323890431631972013758894777140309741935129390297015768713582643582348068171065563802650256565960065201047582581116691865279018481443083634133404456871734224588349932851240939902278852857772240874090103073366139194999398085913528002041787949791674995548809236904350253066698952181458954491521587600763302070101055709667346467178274981943340531021522745779841173173100876264702725095018516677824199733818142804282300961031747257156885200868283262162552850804621026592584042666050361511605692823276578862937221646249263387226746019107252022914312737706377194312459934216793950004099882178225338416417778346328972245140417878899256520398132742852152375062744008410226501238905946472866254396245283539337213331252101745238242758925097416813000735388071657272215630320845974338897771656555041763973550598236914984192269963986648377816228129380629038592368286598983134185222188166799690901539746460579787203029749770287168278013060365079064355083674125148509306763746692482514248137552297251646678328412875814259875291593503972546227382158958840236846540788238035029805960355494051129276122800357236219110138260911217725961189217381637291618712318040507760126364551859509154428340741101957780412277285523094815506659402101296679158236819278380864803586590895640024979637785001490636069382753310067262080662008374551411237242197309339195940406115487850125037507130818294504053083257596428408392560703205549675447339493801368201043030474759039019262194914203062392087296343396390456485525267865121108385382592954860634880962986192817267439352666847666157489902571135514525578918745486509804822731475416985234489977625440493130772022775991189276290027329665587185531530543206454536574667659589367011127224587509531212717181754469270666867381661716929585894830103402709693499856666380325736846063373961108804197128223849684167133844214462156301412887963361227739403071660047619772673714272672989566030756704912026202806904414756141050877173820467126821863214589542154214507220674770172074480518122963953827143784132131825710319355839177131961519962164573936360504510921943071620721478225732241696092470545416711449091231197409947570800111816988728323993927748087694375103322002551786532908225959918071812885153862720547036040176234585041356183336408463671774717112402126019277392052386242595710299970836973058795919985261217215532463448040471027097475516349207504695405799630225500316467665530686280496720382451548335866755027079968901476069244547605637114883551093053617935204887355249377970202741626315706124691277605917831773229234758966829205297909455982503322015748307649778207164102031977513233675014015393778284541528252684639556356440622270627351404365171320231057321728478194017365783885424667313087866411074216194345063051555381054064172603323211738852864410237985108247729834346660957553062456473858762927866393100596426722935498446318146888420749640452411934666892103469174673382975989047523845717274521847403271371069986619972541545134253842186788375129293688126572862115387719324965105989737112901937915991076889355410549661930187265953467401303864531898460980266601723832501816138179044919871086722233148280249031510561730844340942342552752797339969515184261652305893177119221594180785248835404646836951992084403389209903064614958108955775017427938729861471896432889143754999514215866581787875780518195492324090030209238689898749847097547332201970042332361179187882205382279489123276038554324542376299699431748417940616697963498398453267693487839206000992468170890787809074465833833196463153840816879434824909726841353267661992302221702086547490035887290185256786019556867916403856180613548209407427428077370808423244926090002052378929146269431360111728923038525642285848635701334548272961940575527578214830306877087295496043094370029968461721725931122906959895776924708699238003938445300122852634182483059734712134447129574528252224219402082590956703689919029030659233330544907648740772169481354518754802501356274798096471385614129581771624648999966708748460996049922163688691654080111831302649842118325696763454547261669119872910738319187020169833406880898955868935770271402602941664640045686162116748081874962518451448492409874925161119248814895310170073897053291015297428406688653811585699741373977572356288036024901500826634896249641517953519216217122274853273198117378197515066390088275104495779954424594351352378367504170370879233091766599335920101029085831254897829565278260692483037266835050591587628531733520004295815824586141378604375739933891004253354434850469067291902679358555199320950619712947450075798438078077536095433962891948700437561843125672228886857921426124413819322261040245713801881593557146574769577389858802213472120864318927179303450317314525085067389652207907199919225422117470334759242721216889842249001301391538464988190653091063509157979978283643327678457770510881671899296170914332781679190799498034101397504720140904971144170292408274384079420894326525665371636278322421273278426258321203221354484372505412649801255887345653780015097250209794039905314799983216469415302328198528313913445705434316109137813061851157444468570986979549798365340231124713689691546533719643932708191712713553794071778556027123472573813780229986257007757249006153795993810131914431558015307058287798711665470068967296921977705979849136618985820319515622500964455777206091165624522977628174471858620757943222409585380288039284091830621551632490845848868973849571519283631765833497270062040941078383405528284681354175222701039860699557935839377689676090845746980725178910930934668502448387038652955220237816656950225719626759719896877157902206341245898584211319540483952858211899601220699880254374817804694710554521371836044904856003205531183610678081768092129075647044919307768538250858692323989632797219472711805573349208751209872914132180689485824 +SDK FixedPointNumber => 10000 at 0.15 accrued 365 per year for 1 years = 11617.9844312822975648 +``` + +The output shows, if the approximation is acceptable, **bignumber.js** and `FixedPointNumber` class results are closer, +but `FixedPointNumber` converge to a solution in the fraction digits, here 20 by default. + +From above, it is evident how much the compound interest formula is convenient for the lender (usually the bank) and not +for the borrower. + +The two snippets above uses the following functions. + +```typescript +// START-SNIPPET: FinancialMath_Functions +// COMPOUND INTEREST FUNCTIONS FOR DIFFERENT DATA TYPES +function interestWithBigNumberType( + P: number, + r: number, + n: number, + t: number +): BigNumber { + const _P = BigNumber(P); + const _r = BigNumber(r); + const _n = BigNumber(n); + const _t = BigNumber(t); + return BigNumber(1).plus(_r.div(n)).pow(_t.times(_n)).times(_P); +} + +function interestWithFixedPointNumberType( + P: number, + r: number, + n: number, + t: number +): FixedPointNumber { + const _P = FixedPointNumber.of(P); + const _r = FixedPointNumber.of(r); + const _n = FixedPointNumber.of(n); + const _t = FixedPointNumber.of(t); + return FixedPointNumber.ONE.plus(_r.div(_n)) + .pow(_t.times(_n)) + .times(_P); +} + +function interestWithNumberType( + P: number, + r: number, + n: number, + t: number +): number { + return (1 + r / n) ** (t * n) * P; +} +``` + +where you can play to adjust the precision in the `interestWithFixedPointNumberType` and `interestWithBigNumberType` +functions, poking with the `.dp` method. + +You can verify math using a +[compound interest calculator](https://www.investor.gov/financial-tools-calculators/calculators/compound-interest-calculator) +online. diff --git a/src/1.Hello_World/FinancialMath.ts b/src/1.Hello_World/FinancialMath.ts index 674ecbf..ac8ede3 100644 --- a/src/1.Hello_World/FinancialMath.ts +++ b/src/1.Hello_World/FinancialMath.ts @@ -83,3 +83,67 @@ r = x.div(FixedPointNumber.ZERO.div(FixedPointNumber.ZERO)); console.log(`0/0 = ${r}`); // END-SNIPPET: FinancialMath_5 + +// START-SNIPPET: FinancialMath_6 +// STEP 10: compute the simple interest reat for 10,000 currency unit at 15% for one year +let P = 10000; // 10,000 $ +let R = 0.15; // 15% interest rate +let N = 1; // interest accrued times per year +let T = 1; // 1 year of investment time +let jsA = interestWithNumberType(P, R, N, T); +console.log( + `JS number => ${P} at ${R} accrued ${N} per year for ${T} years = ${jsA} ` +); +const bnA = interestWithBigNumberType(P, R, N, T); +console.log( + `BigNumber => ${P} at ${R} accrued ${N} per year for ${T} years = ${bnA} ` +); +const fpA = interestWithFixedPointNumberType(P, R, N, T); +console.log( + `SDK FixedPointNumber => ${P} at ${R} accrued ${N} per year for ${T} years = ${fpA} ` +); + +// END-SNIPPET: FinancialMath_6 + +// START-SNIPPET: FinancialMath_Functions +// COMPOUND INTEREST FUNCTIONS FOR DIFFERENT DATA TYPES +function interestWithBigNumberType( + P: number, + r: number, + n: number, + t: number +): BigNumber { + const _P = BigNumber(P); + const _r = BigNumber(r); + const _n = BigNumber(n); + const _t = BigNumber(t); + return BigNumber(1).plus(_r.div(n)).pow(_t.times(_n)).times(_P); +} + +function interestWithFixedPointNumberType( + P: number, + r: number, + n: number, + t: number +): FixedPointNumber { + const _P = FixedPointNumber.of(P); + const _r = FixedPointNumber.of(r); + const _n = FixedPointNumber.of(n); + const _t = FixedPointNumber.of(t); + return FixedPointNumber.ONE.plus(_r.div(_n)) + .pow(_t.times(_n)) + .times(_P); +} + +function interestWithNumberType( + P: number, + r: number, + n: number, + t: number +): number { + return (1 + r / n) ** (t * n) * P; +} + +// END-SNIPPET: FinancialMath_Functions + + From 401464ad668f3f5184cc7f896cc2e7be2f96149e Mon Sep 17 00:00:00 2001 From: lucanicoladebiasi Date: Mon, 4 Nov 2024 17:49:22 +0000 Subject: [PATCH 15/15] feat: financial math lesson --- package.json | 3 +- src/1.Hello_World/FinancialMath.ts | 54 +++++++++++++++--------------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index edccd58..3166e12 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "license": "MIT", "description": "VeChain SDK Tutorial for NodeJS.", "dependencies": { - "@vechain/sdk-network": "1.0.0-beta.32", + "@vechain/sdk-core": "1.0.0-rc.1", + "@vechain/sdk-network": "1.0.0-rc.1", "bignumber.js": "^9.1.1" }, "devDependencies": { diff --git a/src/1.Hello_World/FinancialMath.ts b/src/1.Hello_World/FinancialMath.ts index ac8ede3..749f7dd 100644 --- a/src/1.Hello_World/FinancialMath.ts +++ b/src/1.Hello_World/FinancialMath.ts @@ -1,12 +1,14 @@ // START-SNIPPET: FinancialMath_1 // STEP 1: import FixedPointNumber and BigNumber to compare results among the two libraries. -import {FixedPointNumber} from '@vechain/sdk-core'; -import {BigNumber} from 'bignumber.js'; +import { FixedPointNumber } from '@vechain/sdk-core'; +import { BigNumber } from 'bignumber.js'; // STEP 2: create two equivalent objects from different expression types and check they are equivalent. let x = FixedPointNumber.of(123.456789); let y = FixedPointNumber.of('123.456789'); -console.log(`FPN value ${x} from number is ${x.isEqual(y) ? '' : 'not'}equal to ${y} from string.`); +console.log( + `FPN value ${x} from number is ${x.isEqual(y) ? '' : 'not'}equal to ${y} from string.` +); // STEP 3: cast to a number console.log(`Cast FPN value to number is ${x.n}.`); @@ -20,19 +22,21 @@ console.log(`Cast FPN value to bigint is ${x.bi}.`); // STEP 5: compute 1/3 comparing JS number, BigNumber and SDK FixedPointNumber math x = FixedPointNumber.of(1); y = FixedPointNumber.of(3); -let r = x.div(y); // r for 'ratio'. -console.log(`${x}/${y} => JS = ${x.n / y.n};\tBigNumber = ${BigNumber(x.n).div(y.n)};\tSDK = ${r}`); +let r = x.div(y); // r for 'ratio'. +console.log( + `${x}/${y} => JS = ${x.n / y.n};\tBigNumber = ${BigNumber(x.n).div(y.n)};\tSDK = ${r}` +); // END-SNIPPET: FinancialMath_2 // START-SNIPPET: FinancialMath_3 // STEP 6: increase the precision to 80 decimal digits. -let fd = 80; // Fractional Digits. -x = x.dp(80); // Force x to fd precision, .div function will adapt y automatically -r = x.div(y); // Ratio -let a = BigNumber(x.n).dp(fd); // Force to fd precision. -let b = BigNumber(y.n).dp(fd); // Force to fd precision -let q = a.div(b) // q for 'quotient' synonymous of 'ratio'. +let fd = 80; // Fractional Digits. +x = x.dp(80); // Force x to fd precision, .div function will adapt y automatically +r = x.div(y); // Ratio +let a = BigNumber(x.n).dp(fd); // Force to fd precision. +const b = BigNumber(y.n).dp(fd); // Force to fd precision +const q = a.div(b); // q for 'quotient' synonymous of 'ratio'. console.log(`${x}/${y} => BigNumber ${q};\t SDK = ${r}`); // END-SNIPPET: FinancialMath_3 @@ -40,16 +44,16 @@ console.log(`${x}/${y} => BigNumber ${q};\t SDK = ${r}`); // START-SNIPPET: FinancialMath_3 // STEP 7: compute the squared root of the natural number from 0 to n. fd = 20; -let n = 8 +let n = 8; let rows = []; for (let i = 0; i <= n; i++) { x = FixedPointNumber.of(i).dp(fd).sqrt(); a = BigNumber(i).dp(fd).sqrt(); rows.push({ 'JS number': Math.sqrt(i), - 'BigNumber': `${a}`, + BigNumber: `${a}`, 'SDK FixedPointNumber': `${x}` - }) + }); } console.table(rows); @@ -58,16 +62,16 @@ console.table(rows); // START-SNIPPET: FinancialMath_4 // STEP 8: compute the squared root of the natural number from 0 to n. fd = 80; -n = 8 +n = 8; rows = []; for (let i = 0; i <= n; i++) { x = FixedPointNumber.of(i).dp(fd).sqrt(); a = BigNumber(i).dp(fd).sqrt(); rows.push({ 'JS number': Math.sqrt(i), - 'BigNumber': `${a}`, + BigNumber: `${a}`, 'SDK FixedPointNumber': `${x}` - }) + }); } console.table(rows); @@ -86,11 +90,11 @@ console.log(`0/0 = ${r}`); // START-SNIPPET: FinancialMath_6 // STEP 10: compute the simple interest reat for 10,000 currency unit at 15% for one year -let P = 10000; // 10,000 $ -let R = 0.15; // 15% interest rate -let N = 1; // interest accrued times per year -let T = 1; // 1 year of investment time -let jsA = interestWithNumberType(P, R, N, T); +const P = 10000; // 10,000 $ +const R = 0.15; // 15% interest rate +const N = 1; // interest accrued times per year +const T = 1; // 1 year of investment time +const jsA = interestWithNumberType(P, R, N, T); console.log( `JS number => ${P} at ${R} accrued ${N} per year for ${T} years = ${jsA} ` ); @@ -130,9 +134,7 @@ function interestWithFixedPointNumberType( const _r = FixedPointNumber.of(r); const _n = FixedPointNumber.of(n); const _t = FixedPointNumber.of(t); - return FixedPointNumber.ONE.plus(_r.div(_n)) - .pow(_t.times(_n)) - .times(_P); + return FixedPointNumber.ONE.plus(_r.div(_n)).pow(_t.times(_n)).times(_P); } function interestWithNumberType( @@ -145,5 +147,3 @@ function interestWithNumberType( } // END-SNIPPET: FinancialMath_Functions - -