From 855f8e0c1442093da001d64cc96f338fabce67d8 Mon Sep 17 00:00:00 2001 From: Paul Puey Date: Fri, 21 Jan 2022 17:01:02 -0800 Subject: [PATCH 1/5] Add full floating point support with divFloat function --- .vscode/launch.json | 36 ++++++++++++++++++++++++++ CHANGELOG.md | 2 ++ src/index.ts | 46 +++++++++++++++++++++++---------- test/biggystring.test.ts | 56 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 13 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..371f7b5 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,36 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}/lib/src/index.js", + "preLaunchTask": "tsc: build - tsconfig.json", + "outFiles": [ + "${workspaceFolder}/lib/**/*.js" + ] + }, + { + "type": "node", + "request": "launch", + "name": "Mocha Tests", + "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", + "args": [ + "-r", + "sucrase/register", + "${workspaceFolder}/test/**/*.test.ts", + ], + "internalConsoleOptions": "openOnSessionStart", + "skipFiles": [ + "/**" + ] + }, + ] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c1d5185..877eaff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- added: divf() to do floating point division with no need to specify precision + ## 4.2.0 (2023-12-05) - added: New toBns function to convert JS number to big number strings diff --git a/src/index.ts b/src/index.ts index dc2d2dc..f7a90ff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,12 +6,13 @@ import BN from 'bn.js' interface ShiftPair { shift: number + extraShift: number x: string y: string } const SCI_NOTATION_REGEX = /^(-?\d*\.?\d*)e((?:\+|-)?\d+)$/ - +const RECOMMENDED_DIVIDE_DIGITS = 77 // ----------------------------------------------------------------------------- // Public // ----------------------------------------------------------------------------- @@ -70,17 +71,25 @@ export function sub( return base === 10 ? out : out.replace(/^(-)?/, '$10x') } +export function divf(x1: string, y1: string): string { + return div(x1, y1, true) +} + export function div( x1: string | number, y1: string | number, - precision: number = 0, + precision: true | number = 0, base: number = 10 ): string { - if (base !== 10 && precision > 0) { + if (base !== 10 && precision !== 0) { throw new Error('Cannot operate on floating point hex values') } if (base !== 10 && base !== 16) throw new Error('Unsupported base') - let { x, y } = floatShifts(x1, y1, precision) + let { x, y, extraShift } = floatShifts( + x1, + y1, + precision === true || precision > 0 + ) const xBase = isHex(x) ? 16 : 10 const yBase = isHex(y) ? 16 : 10 x = cropHex(x) @@ -88,7 +97,10 @@ export function div( const xBN = new BN(x, xBase) const yBN = new BN(y, yBase) let out = xBN.div(yBN).toString(base) - out = addDecimal(out, precision) + out = addDecimal(out, extraShift) + if (typeof precision === 'number' && precision > 0) { + out = toFixed(out, 0, precision) + } return base === 10 ? out : out.replace(/^(-)?/, '$10x') } @@ -323,10 +335,11 @@ function cropHex(x: string): string { function floatShifts( xStart: string | number, yStart: string | number, - moreShift?: number + doFloat: boolean = false ): ShiftPair { let x = toBns(xStart) let y = toBns(yStart) + let xPos: number = x.indexOf('.') let yPos: number = y.indexOf('.') @@ -345,7 +358,7 @@ function floatShifts( yPos = y.indexOf('.') } - if (xPos !== -1 || yPos !== -1 || typeof moreShift === 'number') { + if (xPos !== -1 || yPos !== -1 || doFloat) { if (xHex || yHex) { throw new Error('Cannot operate on base16 float values') } @@ -362,15 +375,21 @@ function floatShifts( } const shift = xShift > yShift ? xShift : yShift - let moreS = 0 - if (typeof moreShift === 'number') { - moreS = moreShift - } - x = addZeros(x.replace('.', ''), shift + moreS - xShift) + x = addZeros(x.replace('.', ''), shift - xShift) y = addZeros(y.replace('.', ''), shift - yShift) - const out: ShiftPair = { x, y, shift } + let extraShift = 0 + if (doFloat) { + const totalLength = x.length + y.length + extraShift = + totalLength > RECOMMENDED_DIVIDE_DIGITS + ? totalLength + : RECOMMENDED_DIVIDE_DIGITS + x = addZeros(x, extraShift) + } + + const out: ShiftPair = { x, y, shift, extraShift } return out } else { @@ -379,6 +398,7 @@ function floatShifts( x, y, shift: 0, + extraShift: 0, } return out } diff --git a/test/biggystring.test.ts b/test/biggystring.test.ts index bc7bec6..20c2d17 100644 --- a/test/biggystring.test.ts +++ b/test/biggystring.test.ts @@ -9,6 +9,7 @@ import { add, ceil, div, + divf, eq, floor, gt, @@ -173,6 +174,61 @@ describe('div', function () { '400000000000000000000000000' ) }) + it('divf with smaller numerator', function () { + assert.equal( + divf('258314', '44259003611849456'), + '0.00000000000583641697552453787845483515333625998899100236535579470378814823369' + ) + }) + it('divf with smaller denominator', function () { + assert.equal( + divf('44259003611849456', '258314'), + '171337997986.36332525530942960892557120403849578420062404670285001974341305542866433875051' + ) + }) + it('divf with decimal numerator', function () { + assert.equal( + divf('4425900361184.9456', '258314'), + '17133799.79863633252553094296089255712040384957842006240467028500197434130554286643387' + ) + }) + it('divf with decimal denominator', function () { + assert.equal( + divf('258314', '4425900361184.9456'), + '0.00000005836416975524537878454835153336259988991002365355794703788148233694743' + ) + }) + it('divf with <1 decimal numerator', function () { + assert.equal( + divf('0.00001234', '258314'), + '0.00000000004777131707921367018434928033323784231594106397640081451256997297862' + ) + }) + it('divf with <1 decimal denominator', function () { + assert.equal( + divf('258314', '0.00001234'), + '20933063209.07617504051863857374392220421393841166936790923824959481361426256077795786061' + ) + }) + it('divf with very big divided by very small', function () { + assert.equal( + divf( + '258314184762834762387462843985739845739845734.123876328756', + '0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001234' + ), + '209330781817532222356128722840956114862111615983692324761750405186385737439222042139384116693679092382495948136142625607779578606158833063209.0761750405186385737439222042139384116693679092382495948136142625607779578606158833063209076175040518638573743922204213938411669367909238249594813614262560777957860615883306320907617504051863857374392220421393841166936790923824959481361426256077' + ) + }) + it('divf with very small divided by very big', function () { + assert.equal( + divf( + '0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001234', + '258314184762834762387462843985739845739845734.123876328756' + ), + '0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047771282910110753934911288073993261089606462593095155090550880308370686890364229227812023898933169363535' + ) + }) + it('very big float (precision 9, base 10)', function () { assert.equal( div('800000000000000000000000000.000000008', '2', 9, 10), From e065b8a60c363f82152dfff933cc531c790b226c Mon Sep 17 00:00:00 2001 From: Paul V Puey Date: Tue, 12 Mar 2024 17:39:35 -0700 Subject: [PATCH 2/5] fixup! Add full floating point support with divFloat function --- .vscode/launch.json | 36 ------------------------------------ src/index.ts | 29 ++++++++++++++++++++++------- 2 files changed, 22 insertions(+), 43 deletions(-) delete mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 371f7b5..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Launch Program", - "skipFiles": [ - "/**" - ], - "program": "${workspaceFolder}/lib/src/index.js", - "preLaunchTask": "tsc: build - tsconfig.json", - "outFiles": [ - "${workspaceFolder}/lib/**/*.js" - ] - }, - { - "type": "node", - "request": "launch", - "name": "Mocha Tests", - "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", - "args": [ - "-r", - "sucrase/register", - "${workspaceFolder}/test/**/*.test.ts", - ], - "internalConsoleOptions": "openOnSessionStart", - "skipFiles": [ - "/**" - ] - }, - ] -} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index f7a90ff..9f988b3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,7 +12,7 @@ interface ShiftPair { } const SCI_NOTATION_REGEX = /^(-?\d*\.?\d*)e((?:\+|-)?\d+)$/ -const RECOMMENDED_DIVIDE_DIGITS = 77 +const TARGET_BIGNUM_DIGITS = 77 // ----------------------------------------------------------------------------- // Public // ----------------------------------------------------------------------------- @@ -330,8 +330,26 @@ function cropHex(x: string): string { return x.replace('0x', '') } -// Takes two floating point (base 10) numbers and finds the multiplier needed to make them both -// operable as a integer +/** + * Takes two numbers and finds the multiplier needed to make them both + * operable as a integer. If the numbers are floating point they must + * be base 10. The ShiftPair return value contains the two numbers shifted + * by a fixed number of decimal places and the number of decimal places + * they were shifted by in the `shift` param. + * + * ShiftPair.extraShift are the extra digits we add to x when doFloat is + * true which is specifically for div so that the numerator (x) can have + * enough precision to be divided by the denominator (y) since the actual + * math is done in integers. + * + * 1/3 would be 0 without extraShift. By adding more digits to '1' we + * get 1000/3 which is 333. When we convert back to float by shifting back + * extraDigits, we get 0.333 + * @param xStart + * @param yStart + * @param doFloat - If true, add extra digits to x to allow for more divide precision + * @returns ShiftPair + */ function floatShifts( xStart: string | number, yStart: string | number, @@ -382,10 +400,7 @@ function floatShifts( let extraShift = 0 if (doFloat) { const totalLength = x.length + y.length - extraShift = - totalLength > RECOMMENDED_DIVIDE_DIGITS - ? totalLength - : RECOMMENDED_DIVIDE_DIGITS + extraShift = Math.max(totalLength, TARGET_BIGNUM_DIGITS) x = addZeros(x, extraShift) } From 62fb61e3b5e23800c2facde986580cbdc92e34c9 Mon Sep 17 00:00:00 2001 From: Samuel Holmes Date: Tue, 27 Feb 2024 13:59:04 -0800 Subject: [PATCH 3/5] Inline `isHex` checks --- src/index.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/index.ts b/src/index.ts index 9f988b3..753a619 100644 --- a/src/index.ts +++ b/src/index.ts @@ -361,9 +361,6 @@ function floatShifts( let xPos: number = x.indexOf('.') let yPos: number = y.indexOf('.') - const xHex: boolean = isHex(x) - const yHex: boolean = isHex(y) - if (xPos !== -1) { // Remove trailing zeros x = trimEnd(x) @@ -377,7 +374,7 @@ function floatShifts( } if (xPos !== -1 || yPos !== -1 || doFloat) { - if (xHex || yHex) { + if (isHex(x) || isHex(y)) { throw new Error('Cannot operate on base16 float values') } From 93fa6300e4ec0f575cc96fa3743c9d772980c8d1 Mon Sep 17 00:00:00 2001 From: Paul Puey Date: Mon, 11 Mar 2024 17:35:28 -0700 Subject: [PATCH 4/5] Add vscode launch.json file --- .vscode/launch.json | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..371f7b5 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,36 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}/lib/src/index.js", + "preLaunchTask": "tsc: build - tsconfig.json", + "outFiles": [ + "${workspaceFolder}/lib/**/*.js" + ] + }, + { + "type": "node", + "request": "launch", + "name": "Mocha Tests", + "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", + "args": [ + "-r", + "sucrase/register", + "${workspaceFolder}/test/**/*.test.ts", + ], + "internalConsoleOptions": "openOnSessionStart", + "skipFiles": [ + "/**" + ] + }, + ] +} \ No newline at end of file From dc27984390c77db02178a8c069c955780030a5b9 Mon Sep 17 00:00:00 2001 From: Paul V Puey Date: Tue, 12 Mar 2024 16:08:37 -0700 Subject: [PATCH 5/5] Rename and expand comment of TARGET_BIGNUM_DIGITS --- src/index.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/index.ts b/src/index.ts index 753a619..e4b6e9b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,7 +12,13 @@ interface ShiftPair { } const SCI_NOTATION_REGEX = /^(-?\d*\.?\d*)e((?:\+|-)?\d+)$/ + +// This is the approximate number of decimal digits that can fit into a 256 +// bit number. For math operations, we expand floating point numbers to +// turn them into integers for use with the BN library. This is the number of +// digits we expand to. const TARGET_BIGNUM_DIGITS = 77 + // ----------------------------------------------------------------------------- // Public // -----------------------------------------------------------------------------