@formatjs/ecma402-abstract
Advanced tools
| import { Decimal } from 'decimal.js'; | ||
| /** | ||
| * Cached function to compute powers of 10 for Decimal.js operations. | ||
| * This cache significantly reduces overhead in ComputeExponent and ToRawFixed | ||
| * by memoizing expensive Decimal.pow(10, n) calculations. | ||
| * | ||
| * Common exponents (e.g., -20 to 20) are used repeatedly in number formatting, | ||
| * so caching provides substantial performance benefits. | ||
| * | ||
| * @param exponent - Can be a number or Decimal. If Decimal, it will be converted to string for cache key. | ||
| */ | ||
| export declare const getPowerOf10: (exponent: number | Decimal) => Decimal; |
| import { Decimal } from 'decimal.js'; | ||
| import { memoize } from '@formatjs/fast-memoize'; | ||
| /** | ||
| * Cached function to compute powers of 10 for Decimal.js operations. | ||
| * This cache significantly reduces overhead in ComputeExponent and ToRawFixed | ||
| * by memoizing expensive Decimal.pow(10, n) calculations. | ||
| * | ||
| * Common exponents (e.g., -20 to 20) are used repeatedly in number formatting, | ||
| * so caching provides substantial performance benefits. | ||
| * | ||
| * @param exponent - Can be a number or Decimal. If Decimal, it will be converted to string for cache key. | ||
| */ | ||
| export var getPowerOf10 = memoize(function (exponent) { | ||
| return Decimal.pow(10, exponent); | ||
| }); |
| import { Decimal } from 'decimal.js'; | ||
| import { ComputeExponentForMagnitude } from './ComputeExponentForMagnitude.js'; | ||
| import { FormatNumericToString } from './FormatNumericToString.js'; | ||
| import { getPowerOf10 } from './decimal-cache.js'; | ||
| /** | ||
@@ -18,6 +19,20 @@ * The abstract operation ComputeExponent computes an exponent (power of ten) by which to scale x | ||
| } | ||
| var magnitude = x.log(10).floor(); | ||
| // Fast path for simple numbers | ||
| // If x can be represented as a safe integer, use native Math.log10 | ||
| var xNum = x.toNumber(); | ||
| var magnitude; | ||
| if (Number.isFinite(xNum) && | ||
| Number.isSafeInteger(xNum) && | ||
| xNum > 0 && | ||
| xNum <= 999999) { | ||
| // Use fast native logarithm for simple positive integers | ||
| var magNum = Math.floor(Math.log10(xNum)); | ||
| magnitude = new Decimal(magNum); | ||
| } | ||
| else { | ||
| magnitude = x.log(10).floor(); | ||
| } | ||
| var exponent = ComputeExponentForMagnitude(internalSlots, magnitude); | ||
| // Preserve more precision by doing multiplication when exponent is negative. | ||
| x = x.times(Decimal.pow(10, -exponent)); | ||
| x = x.times(getPowerOf10(-exponent)); | ||
| var formatNumberResult = FormatNumericToString(internalSlots, x); | ||
@@ -27,3 +42,15 @@ if (formatNumberResult.roundedNumber.isZero()) { | ||
| } | ||
| var newMagnitude = formatNumberResult.roundedNumber.log(10).floor(); | ||
| // Fast path for simple rounded numbers | ||
| var roundedNum = formatNumberResult.roundedNumber.toNumber(); | ||
| var newMagnitude; | ||
| if (Number.isFinite(roundedNum) && | ||
| Number.isSafeInteger(roundedNum) && | ||
| roundedNum > 0 && | ||
| roundedNum <= 999999) { | ||
| var newMagNum = Math.floor(Math.log10(roundedNum)); | ||
| newMagnitude = new Decimal(newMagNum); | ||
| } | ||
| else { | ||
| newMagnitude = formatNumberResult.roundedNumber.log(10).floor(); | ||
| } | ||
| if (newMagnitude.eq(magnitude.minus(exponent))) { | ||
@@ -30,0 +57,0 @@ return [exponent, magnitude.toNumber()]; |
| import { Decimal } from 'decimal.js'; | ||
| import { invariant } from '../utils.js'; | ||
| import { getPowerOf10 } from './decimal-cache.js'; | ||
| Decimal.set({ | ||
@@ -40,3 +41,3 @@ toExpPos: 100, | ||
| } | ||
| var num = Decimal.pow(10, magnitude).toString(); | ||
| var num = getPowerOf10(magnitude).toString(); | ||
| var thresholds = Object.keys(thresholdMap); // TODO: this can be pre-processed | ||
@@ -43,0 +44,0 @@ if (num < thresholds[0]) { |
| import { Decimal } from 'decimal.js'; | ||
| import { S_UNICODE_REGEX } from '../regex.generated.js'; | ||
| import { getPowerOf10 } from './decimal-cache.js'; | ||
| import { digitMapping } from './digit-mapping.generated.js'; | ||
@@ -166,5 +167,3 @@ import { GetUnsignedRoundingMode } from './GetUnsignedRoundingMode.js'; | ||
| if (currencyNameData) { | ||
| unitName = selectPlural(pl, numberResult.roundedNumber | ||
| .times(Decimal.pow(10, exponent)) | ||
| .toNumber(), currencyNameData.displayName); | ||
| unitName = selectPlural(pl, numberResult.roundedNumber.times(getPowerOf10(exponent)).toNumber(), currencyNameData.displayName); | ||
| } | ||
@@ -206,5 +205,3 @@ else { | ||
| // Simple unit pattern | ||
| unitPattern = selectPlural(pl, numberResult.roundedNumber | ||
| .times(Decimal.pow(10, exponent)) | ||
| .toNumber(), data.units.simple[unit][unitDisplay]); | ||
| unitPattern = selectPlural(pl, numberResult.roundedNumber.times(getPowerOf10(exponent)).toNumber(), data.units.simple[unit][unitDisplay]); | ||
| } | ||
@@ -217,5 +214,3 @@ else { | ||
| unitData = data.units.simple[numeratorUnit]; | ||
| var numeratorUnitPattern = selectPlural(pl, numberResult.roundedNumber | ||
| .times(Decimal.pow(10, exponent)) | ||
| .toNumber(), data.units.simple[numeratorUnit][unitDisplay]); | ||
| var numeratorUnitPattern = selectPlural(pl, numberResult.roundedNumber.times(getPowerOf10(exponent)).toNumber(), data.units.simple[numeratorUnit][unitDisplay]); | ||
| var perUnitPattern = data.units.simple[denominatorUnit].perUnit[unitDisplay]; | ||
@@ -222,0 +217,0 @@ if (perUnitPattern) { |
@@ -1,2 +0,1 @@ | ||
| import { Decimal } from 'decimal.js'; | ||
| import { invariant } from '../utils.js'; | ||
@@ -6,2 +5,3 @@ import { ComputeExponent } from './ComputeExponent.js'; | ||
| import { FormatNumericToString } from './FormatNumericToString.js'; | ||
| import { getPowerOf10 } from './decimal-cache.js'; | ||
| /** | ||
@@ -49,3 +49,3 @@ * https://tc39.es/ecma402/#sec-partitionnumberpattern | ||
| // 8.d. Let x be x × 10^(-exponent). | ||
| x = x.times(Decimal.pow(10, -exponent)); | ||
| x = x.times(getPowerOf10(-exponent)); | ||
| } | ||
@@ -52,0 +52,0 @@ // 8.e. Let formatNumberResult be FormatNumericToString(internalSlots, x). |
| import { Decimal } from 'decimal.js'; | ||
| import { repeat } from '../utils.js'; | ||
| import { ApplyUnsignedRoundingMode } from './ApplyUnsignedRoundingMode.js'; | ||
| import { getPowerOf10 } from './decimal-cache.js'; | ||
| //IMPL: Setting Decimal configuration | ||
@@ -10,7 +11,7 @@ Decimal.set({ | ||
| function ToRawFixedFn(n, f) { | ||
| return n.times(Decimal.pow(10, -f)); | ||
| return n.times(getPowerOf10(-f)); | ||
| } | ||
| //IMPL: Helper function to find n1 and r1 | ||
| function findN1R1(x, f, roundingIncrement) { | ||
| var nx = x.times(Decimal.pow(10, f)).floor(); | ||
| var nx = x.times(getPowerOf10(f)).floor(); | ||
| var n1 = nx.div(roundingIncrement).floor().times(roundingIncrement); | ||
@@ -25,3 +26,3 @@ var r1 = ToRawFixedFn(n1, f); | ||
| function findN2R2(x, f, roundingIncrement) { | ||
| var nx = x.times(Decimal.pow(10, f)).ceil(); | ||
| var nx = x.times(getPowerOf10(f)).ceil(); | ||
| var n2 = nx.div(roundingIncrement).ceil().times(roundingIncrement); | ||
@@ -28,0 +29,0 @@ var r2 = ToRawFixedFn(n2, f); |
@@ -1,15 +0,50 @@ | ||
| import { Decimal } from 'decimal.js'; | ||
| import { ZERO } from '../constants.js'; | ||
| import { invariant, repeat } from '../utils.js'; | ||
| import { ApplyUnsignedRoundingMode } from './ApplyUnsignedRoundingMode.js'; | ||
| //IMPL: Helper function to find n1, e1, and r1 | ||
| import { getPowerOf10 } from './decimal-cache.js'; | ||
| //IMPL: Helper function to find n1, e1, and r1 using direct calculation | ||
| function findN1E1R1(x, p) { | ||
| var maxN1 = Decimal.pow(10, p); | ||
| var minN1 = Decimal.pow(10, p - 1); | ||
| var maxN1 = getPowerOf10(p); | ||
| var minN1 = getPowerOf10(p - 1); | ||
| // Direct calculation: compute e1 from logarithm | ||
| // e1 is the exponent such that n1 * 10^(e1-p+1) <= x | ||
| // Taking log: log(n1) + (e1-p+1)*log(10) <= log(x) | ||
| // Since n1 is between 10^(p-1) and 10^p, we have: | ||
| // (p-1) + (e1-p+1) <= log10(x) < p + (e1-p+1) | ||
| // Simplifying: e1 <= log10(x) < e1 + 1 | ||
| // Therefore: e1 = floor(log10(x)) | ||
| var log10x = x.log(10); | ||
| var e1 = log10x.floor(); | ||
| // Calculate n1 and r1 from e1 | ||
| var divisor = getPowerOf10(e1.minus(p).plus(1)); | ||
| var n1 = x.div(divisor).floor(); | ||
| var r1 = n1.times(divisor); | ||
| // Verify and adjust if n1 is out of bounds | ||
| // This handles edge cases near powers of 10 | ||
| if (n1.greaterThanOrEqualTo(maxN1)) { | ||
| e1 = e1.plus(1); | ||
| var newDivisor = getPowerOf10(e1.minus(p).plus(1)); | ||
| n1 = x.div(newDivisor).floor(); | ||
| r1 = n1.times(newDivisor); | ||
| } | ||
| else if (n1.lessThan(minN1)) { | ||
| e1 = e1.minus(1); | ||
| var newDivisor = getPowerOf10(e1.minus(p).plus(1)); | ||
| n1 = x.div(newDivisor).floor(); | ||
| r1 = n1.times(newDivisor); | ||
| } | ||
| // Final verification with fallback to iterative search if needed | ||
| if (r1.lessThanOrEqualTo(x) && | ||
| n1.lessThan(maxN1) && | ||
| n1.greaterThanOrEqualTo(minN1)) { | ||
| return { n1: n1, e1: e1, r1: r1 }; | ||
| } | ||
| // Fallback: iterative search (should rarely be needed) | ||
| var maxE1 = x.div(minN1).log(10).plus(p).minus(1).ceil(); | ||
| var currentE1 = maxE1; | ||
| while (true) { | ||
| var currentN1 = x.div(Decimal.pow(10, currentE1.minus(p).plus(1))).floor(); | ||
| var currentDivisor = getPowerOf10(currentE1.minus(p).plus(1)); | ||
| var currentN1 = x.div(currentDivisor).floor(); | ||
| if (currentN1.lessThan(maxN1) && currentN1.greaterThanOrEqualTo(minN1)) { | ||
| var currentR1 = currentN1.times(Decimal.pow(10, currentE1.minus(p).plus(1))); | ||
| var currentR1 = currentN1.times(currentDivisor); | ||
| if (currentR1.lessThanOrEqualTo(x)) { | ||
@@ -26,12 +61,40 @@ return { | ||
| } | ||
| //IMPL: Helper function to find n2, e2, and r2 | ||
| //IMPL: Helper function to find n2, e2, and r2 using direct calculation | ||
| function findN2E2R2(x, p) { | ||
| var maxN2 = Decimal.pow(10, p); | ||
| var minN2 = Decimal.pow(10, p - 1); | ||
| var maxN2 = getPowerOf10(p); | ||
| var minN2 = getPowerOf10(p - 1); | ||
| // Direct calculation: similar to findN1E1R1 but with ceiling | ||
| var log10x = x.log(10); | ||
| var e2 = log10x.floor(); | ||
| // Calculate n2 and r2 from e2 | ||
| var divisor = getPowerOf10(e2.minus(p).plus(1)); | ||
| var n2 = x.div(divisor).ceil(); | ||
| var r2 = n2.times(divisor); | ||
| // Verify and adjust if n2 is out of bounds | ||
| if (n2.greaterThanOrEqualTo(maxN2)) { | ||
| e2 = e2.plus(1); | ||
| var newDivisor = getPowerOf10(e2.minus(p).plus(1)); | ||
| n2 = x.div(newDivisor).ceil(); | ||
| r2 = n2.times(newDivisor); | ||
| } | ||
| else if (n2.lessThan(minN2)) { | ||
| e2 = e2.minus(1); | ||
| var newDivisor = getPowerOf10(e2.minus(p).plus(1)); | ||
| n2 = x.div(newDivisor).ceil(); | ||
| r2 = n2.times(newDivisor); | ||
| } | ||
| // Final verification with fallback to iterative search if needed | ||
| if (r2.greaterThanOrEqualTo(x) && | ||
| n2.lessThan(maxN2) && | ||
| n2.greaterThanOrEqualTo(minN2)) { | ||
| return { n2: n2, e2: e2, r2: r2 }; | ||
| } | ||
| // Fallback: iterative search (should rarely be needed) | ||
| var minE2 = x.div(maxN2).log(10).plus(p).minus(1).floor(); | ||
| var currentE2 = minE2; | ||
| while (true) { | ||
| var currentN2 = x.div(Decimal.pow(10, currentE2.minus(p).plus(1))).ceil(); | ||
| var currentDivisor = getPowerOf10(currentE2.minus(p).plus(1)); | ||
| var currentN2 = x.div(currentDivisor).ceil(); | ||
| if (currentN2.lessThan(maxN2) && currentN2.greaterThanOrEqualTo(minN2)) { | ||
| var currentR2 = currentN2.times(Decimal.pow(10, currentE2.minus(p).plus(1))); | ||
| var currentR2 = currentN2.times(currentDivisor); | ||
| if (currentR2.greaterThanOrEqualTo(x)) { | ||
@@ -38,0 +101,0 @@ return { |
+1
-1
| { | ||
| "name": "@formatjs/ecma402-abstract", | ||
| "description": "A collection of implementation for ECMAScript abstract operations", | ||
| "version": "3.0.3", | ||
| "version": "3.0.4", | ||
| "license": "MIT", | ||
@@ -6,0 +6,0 @@ "author": "Long Ho <holevietlong@gmail.com", |
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
155521
3.05%103
1.98%4323
2.71%