@formatjs/ecma402-abstract
Version:
A collection of implementation for ECMAScript abstract operations
52 lines (51 loc) • 2.1 kB
JavaScript
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()];
}