import Decimal from 'decimal.js'; import { ZERO } from '../constants'; import { invariant, repeat } from '../utils'; import { ApplyUnsignedRoundingMode } from './ApplyUnsignedRoundingMode'; //IMPL: Helper function to find n1, e1, and r1 function findN1E1R1(x, p) { var maxN1 = Decimal.pow(10, p); var minN1 = Decimal.pow(10, p - 1); 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(); if (currentN1.lessThan(maxN1) && currentN1.greaterThanOrEqualTo(minN1)) { var currentR1 = currentN1.times(Decimal.pow(10, currentE1.minus(p).plus(1))); if (currentR1.lessThanOrEqualTo(x)) { return { n1: currentN1, e1: currentE1, r1: currentR1, }; } } currentE1 = currentE1.minus(1); } } //IMPL: Helper function to find n2, e2, and r2 function findN2E2R2(x, p) { var maxN2 = Decimal.pow(10, p); var minN2 = Decimal.pow(10, p - 1); 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(); if (currentN2.lessThan(maxN2) && currentN2.greaterThanOrEqualTo(minN2)) { var currentR2 = currentN2.times(Decimal.pow(10, currentE2.minus(p).plus(1))); if (currentR2.greaterThanOrEqualTo(x)) { return { n2: currentN2, e2: currentE2, r2: currentR2, }; } } currentE2 = currentE2.plus(1); } } /** * https://tc39.es/ecma402/#sec-torawprecision * @param x a finite non-negative Number or BigInt * @param minPrecision an integer between 1 and 21 * @param maxPrecision an integer between 1 and 21 */ export function ToRawPrecision(x, minPrecision, maxPrecision, unsignedRoundingMode) { // 1. Let p be maxPrecision. var p = maxPrecision; var m; var e; var xFinal; // 2. If x = 0, then if (x.isZero()) { // a. Let m be the String value consisting of p occurrences of the character "0". m = repeat('0', p); // b. Let e be 0. e = 0; // c. Let xFinal be 0. xFinal = ZERO; } else { // 3. Else, // a. Let {n1, e1, r1} be the result of findN1E1R1(x, p). var _a = findN1E1R1(x, p), n1 = _a.n1, e1 = _a.e1, r1 = _a.r1; // b. Let {n2, e2, r2} be the result of findN2E2R2(x, p). var _b = findN2E2R2(x, p), n2 = _b.n2, e2 = _b.e2, r2 = _b.r2; // c. Let r be ApplyUnsignedRoundingMode(x, r1, r2, unsignedRoundingMode). var r = ApplyUnsignedRoundingMode(x, r1, r2, unsignedRoundingMode); var n // d. If r = r1, then = void 0; // d. If r = r1, then if (r.eq(r1)) { // i. Let n be n1. n = n1; // ii. Let e be e1. e = e1.toNumber(); // iii. Let xFinal be r1. xFinal = r1; } else { // e. Else, // i. Let n be n2. n = n2; // ii. Let e be e2. e = e2.toNumber(); // iii. Let xFinal be r2. xFinal = r2; } // f. Let m be the String representation of n. m = n.toString(); } var int; // 4. If e ≥ p - 1, then if (e >= p - 1) { // a. Let m be the string-concatenation of m and p - 1 - e occurrences of the character "0". m = m + repeat('0', e - p + 1); // b. Let int be e + 1. int = e + 1; } else if (e >= 0) { // 5. Else if e ≥ 0, then // a. Let m be the string-concatenation of the first e + 1 characters of m, ".", and the remaining p - (e + 1) characters of m. m = m.slice(0, e + 1) + '.' + m.slice(m.length - (p - (e + 1))); // b. Let int be e + 1. int = e + 1; } else { // 6. Else, // a. Assert: e < 0. invariant(e < 0, 'e should be less than 0'); // b. Let m be the string-concatenation of "0.", -e - 1 occurrences of the character "0", and m. m = '0.' + repeat('0', -e - 1) + m; // c. Let int be 1. int = 1; } // 7. If m contains ".", and maxPrecision > minPrecision, then if (m.includes('.') && maxPrecision > minPrecision) { // a. Let cut be maxPrecision - minPrecision. var cut = maxPrecision - minPrecision; // b. Repeat, while cut > 0 and the last character of m is "0", while (cut > 0 && m[m.length - 1] === '0') { // i. Remove the last character from m. m = m.slice(0, m.length - 1); // ii. Decrease cut by 1. cut--; } // c. If the last character of m is ".", then if (m[m.length - 1] === '.') { // i. Remove the last character from m. m = m.slice(0, m.length - 1); } } // 8. Return the Record { [[FormattedString]]: m, [[RoundedNumber]]: xFinal, [[IntegerDigitsCount]]: int, [[RoundingMagnitude]]: e }. return { formattedString: m, roundedNumber: xFinal, integerDigitsCount: int, roundingMagnitude: e, }; }