UNPKG

@formatjs/ecma402-abstract

Version:

A collection of implementation for ECMAScript abstract operations

52 lines (51 loc) 2.1 kB
import { Decimal } from "@formatjs/bigdecimal"; import "../types/number.js"; import { ComputeExponentForMagnitude } from "./ComputeExponentForMagnitude.js"; import { FormatNumericToString } from "./FormatNumericToString.js"; import { getPowerOf10 } from "./decimal-cache.js"; /** * The abstract operation ComputeExponent computes an exponent (power of ten) by which to scale x * according to the number formatting settings. It handles cases such as 999 rounding up to 1000, * requiring a different exponent. * * NOT IN SPEC: it returns [exponent, magnitude]. */ export function ComputeExponent(internalSlots, x) { if (x.isZero()) { return [0, 0]; } if (x.isNegative()) { x = x.negated(); } // Fast path for simple numbers // If x can be represented as a safe integer, use native Math.log10 const xNum = x.toNumber(); let magnitude; if (Number.isFinite(xNum) && Number.isSafeInteger(xNum) && xNum > 0 && xNum <= 999999) { // Use fast native logarithm for simple positive integers const magNum = Math.floor(Math.log10(xNum)); magnitude = new Decimal(magNum); } else { magnitude = x.log(10).floor(); } const exponent = ComputeExponentForMagnitude(internalSlots, magnitude); // Preserve more precision by doing multiplication when exponent is negative. x = x.times(getPowerOf10(-exponent)); const formatNumberResult = FormatNumericToString(internalSlots, x); if (formatNumberResult.roundedNumber.isZero()) { return [exponent, magnitude.toNumber()]; } // Fast path for simple rounded numbers const roundedNum = formatNumberResult.roundedNumber.toNumber(); let newMagnitude; if (Number.isFinite(roundedNum) && Number.isSafeInteger(roundedNum) && roundedNum > 0 && roundedNum <= 999999) { const newMagNum = Math.floor(Math.log10(roundedNum)); newMagnitude = new Decimal(newMagNum); } else { newMagnitude = formatNumberResult.roundedNumber.log(10).floor(); } if (newMagnitude.eq(magnitude.minus(exponent))) { return [exponent, magnitude.toNumber()]; } return [ComputeExponentForMagnitude(internalSlots, magnitude.plus(1)), magnitude.plus(1).toNumber()]; }