diff --git a/src/Decimal128.mts b/src/Decimal128.mts index a11c73a..f32e4d6 100644 --- a/src/Decimal128.mts +++ b/src/Decimal128.mts @@ -23,7 +23,9 @@ import { Rational } from "./Rational.mjs"; import { Decimal } from "./Decimal.mjs"; const EXPONENT_MIN = -6176; +const NORMAL_EXPONENT_MIN = -6143; const EXPONENT_MAX = 6111; +const NORMAL_EXPONENT_MAX = 6144; const MAX_SIGNIFICANT_DIGITS = 34; const bigTen = BigInt(10); @@ -1046,6 +1048,70 @@ export class Decimal128 { let q = this.divide(d).round(0, ROUNDING_MODE_TRUNCATE); return this.subtract(d.multiply(q)); } + + isNormal(): boolean { + if (this.isNaN()) { + throw new RangeError("Cannot determine whether NaN is normal"); + } + + if (!this.isFinite()) { + throw new RangeError( + "Only finite numbers can be said to be normal or not" + ); + } + + if (this.isZero()) { + throw new RangeError( + "Only non-zero numbers can be said to be normal or not" + ); + } + + let exp = this.exponent(); + return exp >= NORMAL_EXPONENT_MIN && exp <= NORMAL_EXPONENT_MAX; + } + + isSubnormal(): boolean { + if (this.isNaN()) { + throw new RangeError("Cannot determine whether NaN is subnormal"); + } + + if (!this.isFinite()) { + throw new RangeError( + "Only finite numbers can be said to be subnormal or not" + ); + } + + let exp = this.exponent(); + return exp < NORMAL_EXPONENT_MIN; + } + + truncatedExponent(): number { + if (this.isZero() || this.isSubnormal()) { + return NORMAL_EXPONENT_MIN; + } + + return this.exponent(); + } + + scaledSignificand(): bigint { + if (this.isNaN()) { + throw new RangeError("NaN does not have a scaled significand"); + } + + if (!this.isFinite()) { + throw new RangeError("Infinity does not have a scaled significand"); + } + + if (this.isZero()) { + return 0n; + } + + let v = this.cohort() as Rational; + let te = this.truncatedExponent(); + let ss = v.scale10(MAX_SIGNIFICANT_DIGITS - 1 - te); + + return ss.numerator; + } } Decimal128.prototype.valueOf = function () {