From 937ec0c5187279a91ddf48a6721124462e0ec736 Mon Sep 17 00:00:00 2001 From: Lou Cyx Date: Fri, 17 Apr 2026 19:12:07 -0500 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=9A=91=EF=B8=8F=20(@coven/math)=20fix?= =?UTF-8?q?=20issue=20with=20infinite=20fractional=20numbers=20in=20`preci?= =?UTF-8?q?seDivide`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- @coven/math/preciseDivide.ts | 39 ++++++++++++++-------- @coven/math/tests/calculate.test.ts | 4 +++ @coven/math/tests/preciseDivide.test.ts | 40 +++++++++++++++++++++++ @coven/math/tests/preciseToNumber.test.ts | 40 +++++++++++++++++++++-- 4 files changed, 107 insertions(+), 16 deletions(-) diff --git a/@coven/math/preciseDivide.ts b/@coven/math/preciseDivide.ts index fb2d438..06d1008 100644 --- a/@coven/math/preciseDivide.ts +++ b/@coven/math/preciseDivide.ts @@ -6,8 +6,14 @@ import type { PreciseFunction } from "./PreciseFunction.ts"; const alwaysInfinity = always(Infinity); +/** + * @internal We have to define a max so we avoid looping forever. + */ +const MAX_DECIMAL_PLACES = 256n; + /** * Curried divide operation using the internal {@linkcode Precise} type. + * Precision for float values has a maximum of 256. * * @example * ```typescript @@ -25,22 +31,27 @@ export const preciseDivide: PreciseFunction = memoFunction( (divisorBase, divisorExponent) => divisorBase === 0n ? alwaysInfinity : ( memoFunction((dividendBase, dividendExponent) => { - let exponent = 0n; - let base = dividendBase / divisorBase; + if (dividendBase === 0n) { + return precise(0n, 0n); + } else { + let exponent = 0n; + let base = dividendBase / divisorBase; - for ( - let dividend = dividendBase; - base * divisorBase !== dividend; - ) { - exponent += 1n; - dividend = dividendBase * 10n ** exponent; - base = dividend / divisorBase; - } + for ( + let dividend = dividendBase; + exponent < MAX_DECIMAL_PLACES + && base * divisorBase !== dividend; + ) { + exponent += 1n; + dividend = dividendBase * 10n ** exponent; + base = dividend / divisorBase; + } - return precise( - base, - dividendExponent - exponent - divisorExponent, - ); + return precise( + base, + dividendExponent - exponent - divisorExponent, + ); + } }) ), ); diff --git a/@coven/math/tests/calculate.test.ts b/@coven/math/tests/calculate.test.ts index bb35b77..a6e6309 100644 --- a/@coven/math/tests/calculate.test.ts +++ b/@coven/math/tests/calculate.test.ts @@ -166,3 +166,7 @@ Deno.test("2 / NaN = NaN", () => Deno.test("Infinity / NaN = NaN", () => assertStrictEquals(calculate(Infinity).dividedBy(NaN).total, NaN), ); + +Deno.test("1 / 3 * 3 = 1", () => + assertStrictEquals(calculate(1).dividedBy(3).times(3).total, 1), +); diff --git a/@coven/math/tests/preciseDivide.test.ts b/@coven/math/tests/preciseDivide.test.ts index 55a35fc..ff126c4 100644 --- a/@coven/math/tests/preciseDivide.test.ts +++ b/@coven/math/tests/preciseDivide.test.ts @@ -83,6 +83,46 @@ Deno.test("1 / 0 = Infinity", () => assertStrictEquals(preciseDivide(0n, 0n)(1n, 0n), Infinity), ); +Deno.test("0 / 2 = 0", () => + assertStrictEquals(preciseDivide(2n, 0n)(0n, 0n), precise(0n, 0n)), +); + +Deno.test( + "1 / 3 = 0.3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333", + () => + assertStrictEquals( + preciseDivide(3n, 0n)(1n, 0n), + precise( + 3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333n, + -256n, + ), + ), +); + +Deno.test( + "22 / 7 = 3.1428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428", + () => + assertStrictEquals( + preciseDivide(7n, 0n)(22n, 0n), + precise( + 31428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428n, + -256n, + ), + ), +); + +Deno.test( + "103993 / 33102 = 3.1415926530119026040722614947737296840070086399613316415926530119026040722614947737296840070086399613316415926530119026040722614947737296840070086399613316415926530119026040722614947737296840070086399613316415926530119026040722614947737296840070086399613316", + () => + assertStrictEquals( + preciseDivide(33102n, 0n)(103993n, 0n), + precise( + 31415926530119026040722614947737296840070086399613316415926530119026040722614947737296840070086399613316415926530119026040722614947737296840070086399613316415926530119026040722614947737296840070086399613316415926530119026040722614947737296840070086399613316n, + -256n, + ), + ), +); + Deno.test("Same Precise returned with same values", () => assertStrictEquals( preciseDivide(2n, 0n)(3n, 0n), diff --git a/@coven/math/tests/preciseToNumber.test.ts b/@coven/math/tests/preciseToNumber.test.ts index ced434c..870cbeb 100644 --- a/@coven/math/tests/preciseToNumber.test.ts +++ b/@coven/math/tests/preciseToNumber.test.ts @@ -1,7 +1,7 @@ import { assertStrictEquals } from "@std/assert"; import { preciseToNumber } from "../preciseToNumber.ts"; -Deno.test("precise(13n) = 13", () => +Deno.test("precise(13n, 0n) = 13", () => assertStrictEquals(preciseToNumber(13n, 0n), 13), ); @@ -9,7 +9,7 @@ Deno.test("precise(13n, 2n) = 1300", () => assertStrictEquals(preciseToNumber(13n, 2n), 1300), ); -Deno.test("precise(-13n) = -13", () => +Deno.test("precise(-13n, 0n) = -13", () => assertStrictEquals(preciseToNumber(-13n, 0n), -13), ); @@ -36,3 +36,39 @@ Deno.test("precise(-13_001n, -1n) = -1300.1", () => Deno.test("precise(-13_000n, 0n) = -13_000", () => assertStrictEquals(preciseToNumber(-13_000n, 0n), -13_000), ); + +Deno.test( + "precise(3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333n, -256n) = 1 / 3", + () => + assertStrictEquals( + preciseToNumber( + 3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333n, + -256n, + ), + 1 / 3, + ), +); + +Deno.test( + "precise(31428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428n, -256n) = 22 / 7", + () => + assertStrictEquals( + preciseToNumber( + 31428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428n, + -256n, + ), + 22 / 7, + ), +); + +Deno.test( + "precise(31415926530119026040722614947737296840070086399613316415926530119026040722614947737296840070086399613316415926530119026040722614947737296840070086399613316415926530119026040722614947737296840070086399613316415926530119026040722614947737296840070086399613316n, -256n) = 103993 / 33102", + () => + assertStrictEquals( + preciseToNumber( + 31415926530119026040722614947737296840070086399613316415926530119026040722614947737296840070086399613316415926530119026040722614947737296840070086399613316415926530119026040722614947737296840070086399613316415926530119026040722614947737296840070086399613316n, + -256n, + ), + 103993 / 33102, + ), +); From 2bf25188eef394dbea3e6269728765787f7113fb Mon Sep 17 00:00:00 2001 From: Lou Cyx Date: Fri, 17 Apr 2026 19:17:14 -0500 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=94=96=20(shared)=20version=20bump.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- @coven/compare/deno.json | 2 +- @coven/constants/deno.json | 2 +- @coven/cron/deno.json | 2 +- @coven/expression/deno.json | 2 +- @coven/iterables/deno.json | 2 +- @coven/math/deno.json | 2 +- @coven/memo/deno.json | 2 +- @coven/pair/deno.json | 2 +- @coven/parsers/deno.json | 2 +- @coven/predicates/deno.json | 2 +- @coven/rules/deno.json | 2 +- @coven/template/deno.json | 2 +- @coven/terminal/deno.json | 2 +- @coven/types/deno.json | 2 +- @coven/utils/deno.json | 2 +- @simulcast/core/deno.json | 2 +- @simulcast/preact/deno.json | 2 +- @simulcast/react/deno.json | 2 +- @simulcast/vue/deno.json | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/@coven/compare/deno.json b/@coven/compare/deno.json index a66c47e..f7970f8 100644 --- a/@coven/compare/deno.json +++ b/@coven/compare/deno.json @@ -2,5 +2,5 @@ "$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json", "exports": "./mod.ts", "name": "@coven/compare", - "version": "0.9.5" + "version": "0.9.6" } diff --git a/@coven/constants/deno.json b/@coven/constants/deno.json index 72ed8df..2492d5c 100644 --- a/@coven/constants/deno.json +++ b/@coven/constants/deno.json @@ -2,5 +2,5 @@ "$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json", "exports": "./mod.ts", "name": "@coven/constants", - "version": "0.9.5" + "version": "0.9.6" } diff --git a/@coven/cron/deno.json b/@coven/cron/deno.json index 263e31b..593001d 100644 --- a/@coven/cron/deno.json +++ b/@coven/cron/deno.json @@ -2,5 +2,5 @@ "$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json", "exports": "./mod.ts", "name": "@coven/cron", - "version": "0.9.5" + "version": "0.9.6" } diff --git a/@coven/expression/deno.json b/@coven/expression/deno.json index b7d7192..784623f 100644 --- a/@coven/expression/deno.json +++ b/@coven/expression/deno.json @@ -2,5 +2,5 @@ "$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json", "exports": "./mod.ts", "name": "@coven/expression", - "version": "0.9.5" + "version": "0.9.6" } diff --git a/@coven/iterables/deno.json b/@coven/iterables/deno.json index 8b0b33e..5b02d54 100644 --- a/@coven/iterables/deno.json +++ b/@coven/iterables/deno.json @@ -5,5 +5,5 @@ "./async": "./async/mod.ts" }, "name": "@coven/iterables", - "version": "0.9.5" + "version": "0.9.6" } diff --git a/@coven/math/deno.json b/@coven/math/deno.json index 1b1f134..32ab27b 100644 --- a/@coven/math/deno.json +++ b/@coven/math/deno.json @@ -2,5 +2,5 @@ "$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json", "exports": "./mod.ts", "name": "@coven/math", - "version": "0.9.5" + "version": "0.9.6" } diff --git a/@coven/memo/deno.json b/@coven/memo/deno.json index 161b49f..ea57824 100644 --- a/@coven/memo/deno.json +++ b/@coven/memo/deno.json @@ -2,5 +2,5 @@ "$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json", "exports": "./mod.ts", "name": "@coven/memo", - "version": "0.9.5" + "version": "0.9.6" } diff --git a/@coven/pair/deno.json b/@coven/pair/deno.json index e773c14..74fe541 100644 --- a/@coven/pair/deno.json +++ b/@coven/pair/deno.json @@ -13,5 +13,5 @@ "react-dom": "npm:react-dom@^19.2.5" }, "name": "@coven/pair", - "version": "0.9.5" + "version": "0.9.6" } diff --git a/@coven/parsers/deno.json b/@coven/parsers/deno.json index 1a0f1ed..a5374c0 100644 --- a/@coven/parsers/deno.json +++ b/@coven/parsers/deno.json @@ -2,5 +2,5 @@ "$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json", "exports": "./mod.ts", "name": "@coven/parsers", - "version": "0.9.5" + "version": "0.9.6" } diff --git a/@coven/predicates/deno.json b/@coven/predicates/deno.json index 1177ccc..4626e1a 100644 --- a/@coven/predicates/deno.json +++ b/@coven/predicates/deno.json @@ -2,5 +2,5 @@ "$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json", "exports": "./mod.ts", "name": "@coven/predicates", - "version": "0.9.5" + "version": "0.9.6" } diff --git a/@coven/rules/deno.json b/@coven/rules/deno.json index 10e83fa..c4f24b1 100644 --- a/@coven/rules/deno.json +++ b/@coven/rules/deno.json @@ -2,5 +2,5 @@ "$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json", "exports": "./mod.ts", "name": "@coven/rules", - "version": "0.9.5" + "version": "0.9.6" } diff --git a/@coven/template/deno.json b/@coven/template/deno.json index 89c27ab..8096aca 100644 --- a/@coven/template/deno.json +++ b/@coven/template/deno.json @@ -2,5 +2,5 @@ "$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json", "exports": "./mod.ts", "name": "@coven/template", - "version": "0.9.5" + "version": "0.9.6" } diff --git a/@coven/terminal/deno.json b/@coven/terminal/deno.json index f1d77aa..17d990f 100644 --- a/@coven/terminal/deno.json +++ b/@coven/terminal/deno.json @@ -2,5 +2,5 @@ "$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json", "exports": "./mod.ts", "name": "@coven/terminal", - "version": "0.9.5" + "version": "0.9.6" } diff --git a/@coven/types/deno.json b/@coven/types/deno.json index 1c4bfe6..9e6c99b 100644 --- a/@coven/types/deno.json +++ b/@coven/types/deno.json @@ -2,5 +2,5 @@ "$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json", "exports": "./mod.ts", "name": "@coven/types", - "version": "0.9.5" + "version": "0.9.6" } diff --git a/@coven/utils/deno.json b/@coven/utils/deno.json index 0293a7c..af28a08 100644 --- a/@coven/utils/deno.json +++ b/@coven/utils/deno.json @@ -2,5 +2,5 @@ "$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json", "exports": "./mod.ts", "name": "@coven/utils", - "version": "0.9.5" + "version": "0.9.6" } diff --git a/@simulcast/core/deno.json b/@simulcast/core/deno.json index 06c4d99..0f820e1 100644 --- a/@simulcast/core/deno.json +++ b/@simulcast/core/deno.json @@ -2,5 +2,5 @@ "$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json", "exports": "./mod.ts", "name": "@simulcast/core", - "version": "0.9.5" + "version": "0.9.6" } diff --git a/@simulcast/preact/deno.json b/@simulcast/preact/deno.json index 06077b7..437f45d 100644 --- a/@simulcast/preact/deno.json +++ b/@simulcast/preact/deno.json @@ -11,5 +11,5 @@ "preact": "npm:preact@^10.29.1" }, "name": "@simulcast/preact", - "version": "0.9.5" + "version": "0.9.6" } diff --git a/@simulcast/react/deno.json b/@simulcast/react/deno.json index 1239196..e9af20b 100644 --- a/@simulcast/react/deno.json +++ b/@simulcast/react/deno.json @@ -12,5 +12,5 @@ "react": "npm:react@^19.2.5" }, "name": "@simulcast/react", - "version": "0.9.5" + "version": "0.9.6" } diff --git a/@simulcast/vue/deno.json b/@simulcast/vue/deno.json index 72c47b3..4393fab 100644 --- a/@simulcast/vue/deno.json +++ b/@simulcast/vue/deno.json @@ -6,5 +6,5 @@ "vue": "npm:vue@^3.5.32" }, "name": "@simulcast/vue", - "version": "0.9.5" + "version": "0.9.6" }