From 03779b70d51c033e4fa8a156918245f10a5af281 Mon Sep 17 00:00:00 2001 From: Paul Puey Date: Fri, 21 Jan 2022 17:01:02 -0800 Subject: [PATCH] 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),