@bignum/core
Advanced tools
Comparing version 0.3.0 to 0.4.0
@@ -1,9 +0,18 @@ | ||
type DivideOptions = { | ||
/** The maximum number of decimal places. */ | ||
type OverflowContext = { | ||
scale: bigint; | ||
precision: bigint; | ||
}; | ||
type IsOverflow = (context: OverflowContext) => boolean | undefined | null; | ||
type MathOptions = { | ||
/** You can specify an overflow test function. */ | ||
overflow?: IsOverflow; | ||
/** @deprecated The maximum number of decimal places. */ | ||
maxDp?: bigint; | ||
/** The maximum number of precision when having decimals. */ | ||
/** @deprecated The maximum number of precision when having decimals. */ | ||
maxDecimalPrecision?: bigint; | ||
}; | ||
type DivideOptions = MathOptions; | ||
type SqrtOptions = MathOptions; | ||
/** This is used in negative pows. */ | ||
type PowOptions = DivideOptions; | ||
type PowOptions = MathOptions; | ||
declare class Internal { | ||
@@ -34,2 +43,3 @@ #private; | ||
scaleByPowerOfTen(n: Internal | Inf): Internal | Inf | null; | ||
sqrt(options?: SqrtOptions): Internal; | ||
trunc(): Internal; | ||
@@ -57,2 +67,3 @@ round(): Internal; | ||
pow(n: Inf | Internal): Internal | Inf; | ||
sqrt(): Inf | null; | ||
scaleByPowerOfTen(n: Inf | Internal): this | null; | ||
@@ -70,3 +81,3 @@ compareTo(n: Inf | Internal): 1 | 0 | -1; | ||
#private; | ||
static valueOf(value: string | number | bigint | boolean | BigNum): BigNum; | ||
static valueOf: typeof valueOf; | ||
constructor(value: string | number | bigint | boolean | BigNum | Internal | Inf | null | undefined); | ||
@@ -93,3 +104,2 @@ /** Returns a number indicating the sign. */ | ||
* Returns a BigNum whose value is (this**n). | ||
* @param options.maxDp The maximum number of decimal places. This is used in negative pows. Default is 20. | ||
*/ | ||
@@ -99,2 +109,4 @@ pow(n: BigNum | string | number | bigint, options?: PowOptions): BigNum; | ||
scaleByPowerOfTen(n: BigNum | string | number | bigint): BigNum; | ||
/** Returns an approximation to the square root of this. */ | ||
sqrt(options?: SqrtOptions): BigNum; | ||
/** Returns a BigNum whose value is the absolute value of this BigNum. */ | ||
@@ -117,3 +129,5 @@ abs(): BigNum; | ||
} | ||
/** Get a BigNum from the given value */ | ||
declare function valueOf(value: string | number | bigint | boolean | BigNum): BigNum; | ||
export { BigNum, type DivideOptions, type PowOptions }; | ||
export { BigNum, type DivideOptions, type IsOverflow, type MathOptions, type OverflowContext, type PowOptions, type SqrtOptions }; |
236
lib/index.js
@@ -25,2 +25,19 @@ // src/index.mts | ||
} | ||
var MOD_DIV_OPT = { overflow: (ctx) => ctx.scale > 0n }; | ||
function parseOFOption(options = {}, currDp = 20n) { | ||
if (options.overflow) { | ||
return options.overflow; | ||
} | ||
const { maxDecimalPrecision, maxDp } = options; | ||
if (maxDecimalPrecision != null) { | ||
const checkPrecision = (ctx) => ctx.scale > 0n && ctx.precision > maxDecimalPrecision; | ||
if (maxDp == null) | ||
return checkPrecision; | ||
return (ctx) => ctx.scale > maxDp || checkPrecision(ctx); | ||
} | ||
if (maxDp != null) | ||
return (ctx) => ctx.scale > maxDp; | ||
const m = currDp < 20n ? 20n : currDp; | ||
return (ctx) => ctx.scale > m; | ||
} | ||
var Internal = class _Internal { | ||
@@ -77,17 +94,9 @@ constructor(intValue, exponent) { | ||
return this; | ||
const alignMultiplicand = new _Internal(10n ** divisor.#dp(), 0n); | ||
const alignMultiplicand = new _Internal(10n ** divisor.#scale(), 0n); | ||
const alignedTarget = this.multiply(alignMultiplicand).#simplify(); | ||
const alignedDivisor = divisor.multiply(alignMultiplicand).#simplify(); | ||
const under = []; | ||
const maxPr = options?.maxDecimalPrecision; | ||
const maxDp = options?.maxDp; | ||
if (maxPr != null) { | ||
under.push((dp, p) => dp <= 0n || BigInt(p) <= maxPr); | ||
} | ||
if (maxDp != null) { | ||
under.push((dp) => dp <= maxDp); | ||
} else if (!under.length) { | ||
const max = alignedTarget.e > -20n ? 20n : -alignedTarget.e; | ||
under.push((dp) => dp <= max); | ||
} | ||
const overflow = parseOFOption( | ||
options, | ||
max(this.#scale(), divisor.#scale()) | ||
); | ||
if (!(alignedTarget.i % alignedDivisor.i)) { | ||
@@ -98,47 +107,44 @@ const candidate = new _Internal( | ||
); | ||
if (under.every((fn) => fn(candidate.#dp(), candidate.#precision()))) | ||
if (!overflow({ | ||
scale: candidate.#scale(), | ||
precision: candidate.#precision() | ||
})) | ||
return candidate; | ||
} | ||
const quotientSign = divisor.signum() === this.signum() ? "" : "-"; | ||
const absTarget = alignedTarget.abs(); | ||
const absDivisor = alignedDivisor.abs().#trunc(); | ||
let remainder = absTarget.i; | ||
const quotientNumbers = []; | ||
const digitExponent = BigInt(length(remainder) - length(absDivisor) + 1); | ||
let powOfTen; | ||
if (digitExponent >= 0n) { | ||
powOfTen = 10n ** digitExponent; | ||
} else { | ||
powOfTen = 1n; | ||
remainder *= 10n ** -digitExponent; | ||
} | ||
let exponent = digitExponent + absTarget.e; | ||
while (remainder > 0n && under.every((fn) => fn(-exponent + 1n, quotientNumbers.length + 1))) { | ||
const iDivisor = abs(alignedDivisor.#trunc()); | ||
let remainder = abs(alignedTarget.i); | ||
const digitExponent = max(length(remainder) - length(iDivisor) + 1n, 0n); | ||
const pow = 10n ** digitExponent; | ||
let digits = 0n; | ||
let exponent = digitExponent + alignedTarget.e; | ||
const overflowCtx = { | ||
get scale() { | ||
return -exponent; | ||
}, | ||
get precision() { | ||
return length(digits); | ||
} | ||
}; | ||
while (remainder > 0n && !overflow(overflowCtx)) { | ||
exponent--; | ||
if (powOfTen > 1n) { | ||
powOfTen /= 10n; | ||
} else { | ||
remainder *= 10n; | ||
} | ||
let n = 0n; | ||
let amount = 0n; | ||
for (let nn = 1n; nn < 10n; nn++) { | ||
const nextAmount = absDivisor * nn * powOfTen; | ||
if (remainder < nextAmount) | ||
break; | ||
n = nn; | ||
amount = nextAmount; | ||
} | ||
if ( | ||
// Ignore leading zero | ||
n || quotientNumbers.length | ||
) { | ||
quotientNumbers.push(n); | ||
remainder *= 10n; | ||
digits *= 10n; | ||
if (remainder < iDivisor * pow) | ||
continue; | ||
for (let n = 9n; n > 0n; n--) { | ||
const amount = iDivisor * n * pow; | ||
if (remainder < amount) | ||
continue; | ||
digits += n; | ||
remainder -= amount; | ||
break; | ||
} | ||
} | ||
return new _Internal( | ||
BigInt(quotientSign + (quotientNumbers.join("") || "0")), | ||
exponent | ||
); | ||
while (overflow(overflowCtx) && digits) { | ||
exponent++; | ||
digits /= 10n; | ||
} | ||
if (divisor.signum() !== this.signum()) | ||
digits = -digits; | ||
return new _Internal(digits, exponent); | ||
} | ||
@@ -150,3 +156,3 @@ modulo(divisor) { | ||
return null; | ||
const times = this.divide(divisor, { maxDp: 0n }); | ||
const times = this.divide(divisor, MOD_DIV_OPT); | ||
return this.subtract(divisor.multiply(times)); | ||
@@ -162,9 +168,9 @@ } | ||
const divideOptions = { | ||
maxDecimalPrecision: 20n, | ||
overflow: (ctx) => ctx.scale > 0n && ctx.precision > 20n, | ||
...options | ||
}; | ||
let result = _Internal.O; | ||
for (let i = bn; i < 0; i++) | ||
result = result.divide(this, divideOptions); | ||
return result; | ||
return _Internal.O.divide( | ||
new _Internal(this.i ** -bn, this.e * -bn), | ||
divideOptions | ||
); | ||
} | ||
@@ -177,2 +183,49 @@ scaleByPowerOfTen(n) { | ||
} | ||
sqrt(options) { | ||
if (!this.i) | ||
return this; | ||
if (this.i < 0n) | ||
throw new Error("Negative number"); | ||
const overflow = parseOFOption(options, this.#scale()); | ||
const decimalLength = this.#scale(); | ||
const decimalLengthIsOdd = decimalLength & 1n; | ||
let remainder = this.#simplify().i; | ||
if (decimalLengthIsOdd) | ||
remainder *= 10n; | ||
let part = 0n; | ||
const digitExponent = length(remainder) / 2n + 1n; | ||
const pow = 100n ** digitExponent; | ||
let digits = 0n; | ||
let exponent = digitExponent - decimalLength / 2n - (decimalLengthIsOdd ? 1n : 0n); | ||
const overflowCtx = { | ||
get scale() { | ||
return -exponent; | ||
}, | ||
get precision() { | ||
return length(digits); | ||
} | ||
}; | ||
while (remainder > 0n && !overflow(overflowCtx)) { | ||
exponent--; | ||
remainder *= 100n; | ||
digits *= 10n; | ||
part *= 10n; | ||
if (remainder < (part + 1n) * pow) | ||
continue; | ||
for (let n = 9n; n > 0n; n--) { | ||
const amount = (part + n) * n * pow; | ||
if (remainder < amount) | ||
continue; | ||
digits += n; | ||
remainder -= amount; | ||
part += n * 2n; | ||
break; | ||
} | ||
} | ||
while (overflow(overflowCtx) && digits) { | ||
exponent++; | ||
digits /= 10n; | ||
} | ||
return new _Internal(digits, exponent); | ||
} | ||
trunc() { | ||
@@ -203,3 +256,3 @@ return !this.i || !this.e ? this : new _Internal(this.#trunc(), 0n); | ||
if (other.inf) | ||
return -other.compareTo(this); | ||
return other.s > 0 ? -1 : 1; | ||
this.#alignExponent(other); | ||
@@ -211,11 +264,12 @@ return compare(this.i, other.i); | ||
return String(this.i); | ||
const integerPart = [...String(this.i)]; | ||
const decimalPart = []; | ||
const sign = this.i < 0n ? integerPart.shift() : ""; | ||
let integer = abs(this.i); | ||
const decimal = []; | ||
for (let n = this.e; n < 0; n++) { | ||
const decimal = integerPart.pop() || "0"; | ||
if (decimalPart.length || decimal !== "0") | ||
decimalPart.unshift(decimal); | ||
const last = integer % 10n; | ||
integer /= 10n; | ||
if (decimal.length || last) | ||
decimal.unshift(last); | ||
} | ||
return `${sign}${integerPart.join("") || "0"}${decimalPart.length ? `.${decimalPart.join("")}` : ""}`; | ||
const sign = this.i < 0n ? "-" : ""; | ||
return `${sign}${integer}${decimal.length ? `.${decimal.join("")}` : ""}`; | ||
} | ||
@@ -229,19 +283,16 @@ toBigInt() { | ||
} | ||
#dp() { | ||
#scale() { | ||
return -this.#simplify().e; | ||
} | ||
#precision() { | ||
let n = String(this.abs().i); | ||
while (n.endsWith("0")) | ||
n = n.slice(0, -1); | ||
return n.length || 1; | ||
let n = this.i; | ||
while (!(n % 10n)) | ||
n /= 10n; | ||
return length(n); | ||
} | ||
#simplify() { | ||
for (let e = -this.e; e > 0n; e--) { | ||
if (this.i % 10n ** e) { | ||
continue; | ||
} | ||
this.#updateExponent(this.e + e); | ||
break; | ||
} | ||
if (!this.e) | ||
return this; | ||
const newE = this.e + length(this.i) - this.#precision(); | ||
this.#updateExponent(min(newE, 0n)); | ||
return this; | ||
@@ -314,2 +365,5 @@ } | ||
} | ||
sqrt() { | ||
return this.s < 0 ? null : this; | ||
} | ||
scaleByPowerOfTen(n) { | ||
@@ -346,4 +400,4 @@ return n.inf ? n.s > 0 ? this : null : this; | ||
#p; | ||
static valueOf(value) { | ||
return valueOf(value); | ||
static { | ||
this.valueOf = valueOf; | ||
} | ||
@@ -355,3 +409,3 @@ constructor(value) { | ||
} | ||
if (value instanceof Internal) { | ||
if (value instanceof Internal || value instanceof Inf) { | ||
this.#p = value; | ||
@@ -364,6 +418,2 @@ return; | ||
} | ||
if (value instanceof Inf) { | ||
this.#p = value; | ||
return; | ||
} | ||
if (value === Infinity) { | ||
@@ -423,3 +473,2 @@ this.#p = Inf.P; | ||
* Returns a BigNum whose value is (this**n). | ||
* @param options.maxDp The maximum number of decimal places. This is used in negative pows. Default is 20. | ||
*/ | ||
@@ -435,2 +484,6 @@ pow(n, options) { | ||
} | ||
/** Returns an approximation to the square root of this. */ | ||
sqrt(options) { | ||
return new _BigNum(this.#p?.sqrt(options)); | ||
} | ||
/** Returns a BigNum whose value is the absolute value of this BigNum. */ | ||
@@ -494,8 +547,17 @@ abs() { | ||
let t = a; | ||
for (let i = 0; ; i++) | ||
for (let i = 1n; ; i++) | ||
if (!(t /= 10n)) | ||
return i; | ||
} | ||
function max(a, b) { | ||
return a >= b ? a : b; | ||
} | ||
function min(a, b) { | ||
return a <= b ? a : b; | ||
} | ||
function abs(a) { | ||
return a >= 0n ? a : -a; | ||
} | ||
export { | ||
BigNum | ||
}; |
{ | ||
"name": "@bignum/core", | ||
"version": "0.3.0", | ||
"version": "0.4.0", | ||
"description": "Arbitrary-precision decimal arithmetic with BigInt.", | ||
@@ -5,0 +5,0 @@ "type": "module", |
@@ -54,7 +54,17 @@ # @bignum/core | ||
| Name | Type | Description | | ||
| :------------------ | :----- | :--------------------------------------------------------------------------------------------------- | | ||
| maxDp | bigint | The maximum number of decimal places. If `maxDecimalPrecision` is not specified, 20n is the default. | | ||
| maxDecimalPrecision | bigint | The maximum number of precision. | | ||
| Name | Type | Description | | ||
| :---------------------- | :------------------- | :------------------------------------------------------------------------------------------------------------------------------- | | ||
| overflow | `(context)=>boolean` | You can specify an overflow test function. By default, if the number of decimal places exceeds 20, it is considered an overflow. | | ||
| ~~maxDp~~ | bigint | **Deprecated**. The maximum number of decimal places. | | ||
| ~~maxDecimalPrecision~~ | bigint | **Deprecated**. The maximum number of precision when having decimals. | | ||
- `context` parameter | ||
An object that contains the following properties: | ||
| Name | Type | Description | | ||
| :-------- | :----- | :---------------------------- | | ||
| scale | bigint | The number of decimal places. | | ||
| precision | bigint | The number of precision. | | ||
### BigNum.prototype.modulo(divisor): BigNum | ||
@@ -78,7 +88,17 @@ | ||
| Name | Type | Description | | ||
| :------------------ | :----- | :---------------------------------------------------------------------- | | ||
| maxDp | bigint | The maximum number of decimal places. | | ||
| maxDecimalPrecision | bigint | The maximum number of precision when having decimals. Default is `20n`. | | ||
| Name | Type | Description | | ||
| :---------------------- | :------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| overflow | `(context)=>boolean` | You can specify an overflow test function. By default, if there are decimals and the number of precisions exceeds 20, it is considered an overflow. | | ||
| ~~maxDp~~ | bigint | **Deprecated**. The maximum number of decimal places. | | ||
| ~~maxDecimalPrecision~~ | bigint | **Deprecated**. The maximum number of precision when having decimals. | | ||
- `context` parameter | ||
An object that contains the following properties: | ||
| Name | Type | Description | | ||
| :-------- | :----- | :---------------------------- | | ||
| scale | bigint | The number of decimal places. | | ||
| precision | bigint | The number of precision. | | ||
### BigNum.prototype.scaleByPowerOfTen(n): BigNum | ||
@@ -90,2 +110,10 @@ | ||
### BigNum.prototype.sqrt([options]): BigNum | ||
Returns an approximation to the square root of this. | ||
If this BigNum is a negative value, an error will be raised. | ||
An object can be given as an option. It's the same option for `divide()`. | ||
### BigNum.prototype.abs(): BigNum | ||
@@ -92,0 +120,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
46197
1244
161