CoolFace
Apppublic

opusdev/vector-similarity-api

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes
decimal128.ts856 linesDownload Raw Back to src
1import { BSONValue } from './bson_value';2import { BSONError } from './error';3import { Long } from './long';4import { type InspectFn, defaultInspect, isUint8Array } from './parser/utils';5import { ByteUtils } from './utils/byte_utils';6 7const PARSE_STRING_REGEXP = /^(\+|-)?(\d+|(\d*\.\d*))?(E|e)?([-+])?(\d+)?$/;8const PARSE_INF_REGEXP = /^(\+|-)?(Infinity|inf)$/i;9const PARSE_NAN_REGEXP = /^(\+|-)?NaN$/i;10 11const EXPONENT_MAX = 6111;12const EXPONENT_MIN = -6176;13const EXPONENT_BIAS = 6176;14const MAX_DIGITS = 34;15 16// Nan value bits as 32 bit values (due to lack of longs)17const NAN_BUFFER = ByteUtils.fromNumberArray(18  [19    0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0020  ].reverse()21);22// Infinity value bits 32 bit values (due to lack of longs)23const INF_NEGATIVE_BUFFER = ByteUtils.fromNumberArray(24  [25    0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0026  ].reverse()27);28const INF_POSITIVE_BUFFER = ByteUtils.fromNumberArray(29  [30    0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0031  ].reverse()32);33 34const EXPONENT_REGEX = /^([-+])?(\d+)?$/;35 36// Extract least significant 5 bits37const COMBINATION_MASK = 0x1f;38// Extract least significant 14 bits39const EXPONENT_MASK = 0x3fff;40// Value of combination field for Inf41const COMBINATION_INFINITY = 30;42// Value of combination field for NaN43const COMBINATION_NAN = 31;44 45// Detect if the value is a digit46function isDigit(value: string): boolean {47  return !isNaN(parseInt(value, 10));48}49 50// Divide two uint128 values51function divideu128(value: { parts: [number, number, number, number] }) {52  const DIVISOR = Long.fromNumber(1000 * 1000 * 1000);53  let _rem = Long.fromNumber(0);54 55  if (!value.parts[0] && !value.parts[1] && !value.parts[2] && !value.parts[3]) {56    return { quotient: value, rem: _rem };57  }58 59  for (let i = 0; i <= 3; i++) {60    // Adjust remainder to match value of next dividend61    _rem = _rem.shiftLeft(32);62    // Add the divided to _rem63    _rem = _rem.add(new Long(value.parts[i], 0));64    value.parts[i] = _rem.div(DIVISOR).low;65    _rem = _rem.modulo(DIVISOR);66  }67 68  return { quotient: value, rem: _rem };69}70 71// Multiply two Long values and return the 128 bit value72function multiply64x2(left: Long, right: Long): { high: Long; low: Long } {73  if (!left && !right) {74    return { high: Long.fromNumber(0), low: Long.fromNumber(0) };75  }76 77  const leftHigh = left.shiftRightUnsigned(32);78  const leftLow = new Long(left.getLowBits(), 0);79  const rightHigh = right.shiftRightUnsigned(32);80  const rightLow = new Long(right.getLowBits(), 0);81 82  let productHigh = leftHigh.multiply(rightHigh);83  let productMid = leftHigh.multiply(rightLow);84  const productMid2 = leftLow.multiply(rightHigh);85  let productLow = leftLow.multiply(rightLow);86 87  productHigh = productHigh.add(productMid.shiftRightUnsigned(32));88  productMid = new Long(productMid.getLowBits(), 0)89    .add(productMid2)90    .add(productLow.shiftRightUnsigned(32));91 92  productHigh = productHigh.add(productMid.shiftRightUnsigned(32));93  productLow = productMid.shiftLeft(32).add(new Long(productLow.getLowBits(), 0));94 95  // Return the 128 bit result96  return { high: productHigh, low: productLow };97}98 99function lessThan(left: Long, right: Long): boolean {100  // Make values unsigned101  const uhleft = left.high >>> 0;102  const uhright = right.high >>> 0;103 104  // Compare high bits first105  if (uhleft < uhright) {106    return true;107  } else if (uhleft === uhright) {108    const ulleft = left.low >>> 0;109    const ulright = right.low >>> 0;110    if (ulleft < ulright) return true;111  }112 113  return false;114}115 116function invalidErr(string: string, message: string) {117  throw new BSONError(`"${string}" is not a valid Decimal128 string - ${message}`);118}119 120/** @public */121export interface Decimal128Extended {122  $numberDecimal: string;123}124 125/**126 * A class representation of the BSON Decimal128 type.127 * @public128 * @category BSONType129 */130export class Decimal128 extends BSONValue {131  get _bsontype(): 'Decimal128' {132    return 'Decimal128';133  }134 135  readonly bytes!: Uint8Array;136 137  /**138   * @param bytes - a buffer containing the raw Decimal128 bytes in little endian order,139   *                or a string representation as returned by .toString()140   */141  constructor(bytes: Uint8Array | string) {142    super();143    if (typeof bytes === 'string') {144      this.bytes = Decimal128.fromString(bytes).bytes;145    } else if (bytes instanceof Uint8Array || isUint8Array(bytes)) {146      if (bytes.byteLength !== 16) {147        throw new BSONError('Decimal128 must take a Buffer of 16 bytes');148      }149      this.bytes = bytes;150    } else {151      throw new BSONError('Decimal128 must take a Buffer or string');152    }153  }154 155  /**156   * Create a Decimal128 instance from a string representation157   *158   * @param representation - a numeric string representation.159   */160  static fromString(representation: string): Decimal128 {161    return Decimal128._fromString(representation, { allowRounding: false });162  }163 164  /**165   * Create a Decimal128 instance from a string representation, allowing for rounding to 34166   * significant digits167   *168   * @example Example of a number that will be rounded169   * ```ts170   * > let d = Decimal128.fromString('37.499999999999999196428571428571375')171   * Uncaught:172   * BSONError: "37.499999999999999196428571428571375" is not a valid Decimal128 string - inexact rounding173   * at invalidErr (/home/wajames/js-bson/lib/bson.cjs:1402:11)174   * at Decimal128.fromStringInternal (/home/wajames/js-bson/lib/bson.cjs:1633:25)175   * at Decimal128.fromString (/home/wajames/js-bson/lib/bson.cjs:1424:27)176   *177   * > d = Decimal128.fromStringWithRounding('37.499999999999999196428571428571375')178   * new Decimal128("37.49999999999999919642857142857138")179   * ```180   * @param representation - a numeric string representation.181   */182  static fromStringWithRounding(representation: string): Decimal128 {183    return Decimal128._fromString(representation, { allowRounding: true });184  }185 186  private static _fromString(representation: string, options: { allowRounding: boolean }) {187    // Parse state tracking188    let isNegative = false;189    let sawSign = false;190    let sawRadix = false;191    let foundNonZero = false;192 193    // Total number of significant digits (no leading or trailing zero)194    let significantDigits = 0;195    // Total number of significand digits read196    let nDigitsRead = 0;197    // Total number of digits (no leading zeros)198    let nDigits = 0;199    // The number of the digits after radix200    let radixPosition = 0;201    // The index of the first non-zero in *str*202    let firstNonZero = 0;203 204    // Digits Array205    const digits = [0];206    // The number of digits in digits207    let nDigitsStored = 0;208    // Insertion pointer for digits209    let digitsInsert = 0;210    // The index of the last digit211    let lastDigit = 0;212 213    // Exponent214    let exponent = 0;215    // The high 17 digits of the significand216    let significandHigh = new Long(0, 0);217    // The low 17 digits of the significand218    let significandLow = new Long(0, 0);219    // The biased exponent220    let biasedExponent = 0;221 222    // Read index223    let index = 0;224 225    // Naively prevent against REDOS attacks.226    // TODO: implementing a custom parsing for this, or refactoring the regex would yield227    //       further gains.228    if (representation.length >= 7000) {229      throw new BSONError('' + representation + ' not a valid Decimal128 string');230    }231 232    // Results233    const stringMatch = representation.match(PARSE_STRING_REGEXP);234    const infMatch = representation.match(PARSE_INF_REGEXP);235    const nanMatch = representation.match(PARSE_NAN_REGEXP);236 237    // Validate the string238    if ((!stringMatch && !infMatch && !nanMatch) || representation.length === 0) {239      throw new BSONError('' + representation + ' not a valid Decimal128 string');240    }241 242    if (stringMatch) {243      // full_match = stringMatch[0]244      // sign = stringMatch[1]245 246      const unsignedNumber = stringMatch[2];247      // stringMatch[3] is undefined if a whole number (ex "1", 12")248      // but defined if a number w/ decimal in it (ex "1.0, 12.2")249 250      const e = stringMatch[4];251      const expSign = stringMatch[5];252      const expNumber = stringMatch[6];253 254      // they provided e, but didn't give an exponent number. for ex "1e"255      if (e && expNumber === undefined) invalidErr(representation, 'missing exponent power');256 257      // they provided e, but didn't give a number before it. for ex "e1"258      if (e && unsignedNumber === undefined) invalidErr(representation, 'missing exponent base');259 260      if (e === undefined && (expSign || expNumber)) {261        invalidErr(representation, 'missing e before exponent');262      }263    }264 265    // Get the negative or positive sign266    if (representation[index] === '+' || representation[index] === '-') {267      sawSign = true;268      isNegative = representation[index++] === '-';269    }270 271    // Check if user passed Infinity or NaN272    if (!isDigit(representation[index]) && representation[index] !== '.') {273      if (representation[index] === 'i' || representation[index] === 'I') {274        return new Decimal128(isNegative ? INF_NEGATIVE_BUFFER : INF_POSITIVE_BUFFER);275      } else if (representation[index] === 'N') {276        return new Decimal128(NAN_BUFFER);277      }278    }279 280    // Read all the digits281    while (isDigit(representation[index]) || representation[index] === '.') {282      if (representation[index] === '.') {283        if (sawRadix) invalidErr(representation, 'contains multiple periods');284 285        sawRadix = true;286        index = index + 1;287        continue;288      }289 290      if (nDigitsStored < MAX_DIGITS) {291        if (representation[index] !== '0' || foundNonZero) {292          if (!foundNonZero) {293            firstNonZero = nDigitsRead;294          }295 296          foundNonZero = true;297 298          // Only store 34 digits299          digits[digitsInsert++] = parseInt(representation[index], 10);300          nDigitsStored = nDigitsStored + 1;301        }302      }303 304      if (foundNonZero) nDigits = nDigits + 1;305      if (sawRadix) radixPosition = radixPosition + 1;306 307      nDigitsRead = nDigitsRead + 1;308      index = index + 1;309    }310 311    if (sawRadix && !nDigitsRead)312      throw new BSONError('' + representation + ' not a valid Decimal128 string');313 314    // Read exponent if exists315    if (representation[index] === 'e' || representation[index] === 'E') {316      // Read exponent digits317      const match = representation.substr(++index).match(EXPONENT_REGEX);318 319      // No digits read320      if (!match || !match[2]) return new Decimal128(NAN_BUFFER);321 322      // Get exponent323      exponent = parseInt(match[0], 10);324 325      // Adjust the index326      index = index + match[0].length;327    }328 329    // Return not a number330    if (representation[index]) return new Decimal128(NAN_BUFFER);331 332    // Done reading input333    // Find first non-zero digit in digits334    if (!nDigitsStored) {335      digits[0] = 0;336      nDigits = 1;337      nDigitsStored = 1;338      significantDigits = 0;339    } else {340      lastDigit = nDigitsStored - 1;341      significantDigits = nDigits;342      if (significantDigits !== 1) {343        while (344          representation[345            firstNonZero + significantDigits - 1 + Number(sawSign) + Number(sawRadix)346          ] === '0'347        ) {348          significantDigits = significantDigits - 1;349        }350      }351    }352 353    // Normalization of exponent354    // Correct exponent based on radix position, and shift significand as needed355    // to represent user input356 357    // Overflow prevention358    if (exponent <= radixPosition && radixPosition > exponent + (1 << 14)) {359      exponent = EXPONENT_MIN;360    } else {361      exponent = exponent - radixPosition;362    }363 364    // Attempt to normalize the exponent365    while (exponent > EXPONENT_MAX) {366      // Shift exponent to significand and decrease367      lastDigit = lastDigit + 1;368      if (lastDigit >= MAX_DIGITS) {369        // Check if we have a zero then just hard clamp, otherwise fail370        if (significantDigits === 0) {371          exponent = EXPONENT_MAX;372          break;373        }374 375        invalidErr(representation, 'overflow');376      }377      exponent = exponent - 1;378    }379 380    if (options.allowRounding) {381      while (exponent < EXPONENT_MIN || nDigitsStored < nDigits) {382        // Shift last digit. can only do this if < significant digits than # stored.383        if (lastDigit === 0 && significantDigits < nDigitsStored) {384          exponent = EXPONENT_MIN;385          significantDigits = 0;386          break;387        }388 389        if (nDigitsStored < nDigits) {390          // adjust to match digits not stored391          nDigits = nDigits - 1;392        } else {393          // adjust to round394          lastDigit = lastDigit - 1;395        }396 397        if (exponent < EXPONENT_MAX) {398          exponent = exponent + 1;399        } else {400          // Check if we have a zero then just hard clamp, otherwise fail401          const digitsString = digits.join('');402          if (digitsString.match(/^0+$/)) {403            exponent = EXPONENT_MAX;404            break;405          }406          invalidErr(representation, 'overflow');407        }408      }409 410      // Round411      // We've normalized the exponent, but might still need to round.412      if (lastDigit + 1 < significantDigits) {413        let endOfString = nDigitsRead;414 415        // If we have seen a radix point, 'string' is 1 longer than we have416        // documented with ndigits_read, so inc the position of the first nonzero417        // digit and the position that digits are read to.418        if (sawRadix) {419          firstNonZero = firstNonZero + 1;420          endOfString = endOfString + 1;421        }422        // if negative, we need to increment again to account for - sign at start.423        if (sawSign) {424          firstNonZero = firstNonZero + 1;425          endOfString = endOfString + 1;426        }427 428        const roundDigit = parseInt(representation[firstNonZero + lastDigit + 1], 10);429        let roundBit = 0;430 431        if (roundDigit >= 5) {432          roundBit = 1;433          if (roundDigit === 5) {434            roundBit = digits[lastDigit] % 2 === 1 ? 1 : 0;435            for (let i = firstNonZero + lastDigit + 2; i < endOfString; i++) {436              if (parseInt(representation[i], 10)) {437                roundBit = 1;438                break;439              }440            }441          }442        }443 444        if (roundBit) {445          let dIdx = lastDigit;446 447          for (; dIdx >= 0; dIdx--) {448            if (++digits[dIdx] > 9) {449              digits[dIdx] = 0;450 451              // overflowed most significant digit452              if (dIdx === 0) {453                if (exponent < EXPONENT_MAX) {454                  exponent = exponent + 1;455                  digits[dIdx] = 1;456                } else {457                  return new Decimal128(isNegative ? INF_NEGATIVE_BUFFER : INF_POSITIVE_BUFFER);458                }459              }460            } else {461              break;462            }463          }464        }465      }466    } else {467      while (exponent < EXPONENT_MIN || nDigitsStored < nDigits) {468        // Shift last digit. can only do this if < significant digits than # stored.469        if (lastDigit === 0) {470          if (significantDigits === 0) {471            exponent = EXPONENT_MIN;472            break;473          }474 475          invalidErr(representation, 'exponent underflow');476        }477 478        if (nDigitsStored < nDigits) {479          if (480            representation[nDigits - 1 + Number(sawSign) + Number(sawRadix)] !== '0' &&481            significantDigits !== 0482          ) {483            invalidErr(representation, 'inexact rounding');484          }485          // adjust to match digits not stored486          nDigits = nDigits - 1;487        } else {488          if (digits[lastDigit] !== 0) {489            invalidErr(representation, 'inexact rounding');490          }491          // adjust to round492          lastDigit = lastDigit - 1;493        }494 495        if (exponent < EXPONENT_MAX) {496          exponent = exponent + 1;497        } else {498          invalidErr(representation, 'overflow');499        }500      }501 502      // Round503      // We've normalized the exponent, but might still need to round.504      if (lastDigit + 1 < significantDigits) {505        // If we have seen a radix point, 'string' is 1 longer than we have506        // documented with ndigits_read, so inc the position of the first nonzero507        // digit and the position that digits are read to.508        if (sawRadix) {509          firstNonZero = firstNonZero + 1;510        }511        // if saw sign, we need to increment again to account for - or + sign at start.512        if (sawSign) {513          firstNonZero = firstNonZero + 1;514        }515 516        const roundDigit = parseInt(representation[firstNonZero + lastDigit + 1], 10);517 518        if (roundDigit !== 0) {519          invalidErr(representation, 'inexact rounding');520        }521      }522    }523 524    // Encode significand525    // The high 17 digits of the significand526    significandHigh = Long.fromNumber(0);527    // The low 17 digits of the significand528    significandLow = Long.fromNumber(0);529 530    // read a zero531    if (significantDigits === 0) {532      significandHigh = Long.fromNumber(0);533      significandLow = Long.fromNumber(0);534    } else if (lastDigit < 17) {535      let dIdx = 0;536      significandLow = Long.fromNumber(digits[dIdx++]);537      significandHigh = new Long(0, 0);538 539      for (; dIdx <= lastDigit; dIdx++) {540        significandLow = significandLow.multiply(Long.fromNumber(10));541        significandLow = significandLow.add(Long.fromNumber(digits[dIdx]));542      }543    } else {544      let dIdx = 0;545      significandHigh = Long.fromNumber(digits[dIdx++]);546 547      for (; dIdx <= lastDigit - 17; dIdx++) {548        significandHigh = significandHigh.multiply(Long.fromNumber(10));549        significandHigh = significandHigh.add(Long.fromNumber(digits[dIdx]));550      }551 552      significandLow = Long.fromNumber(digits[dIdx++]);553 554      for (; dIdx <= lastDigit; dIdx++) {555        significandLow = significandLow.multiply(Long.fromNumber(10));556        significandLow = significandLow.add(Long.fromNumber(digits[dIdx]));557      }558    }559 560    const significand = multiply64x2(significandHigh, Long.fromString('100000000000000000'));561    significand.low = significand.low.add(significandLow);562 563    if (lessThan(significand.low, significandLow)) {564      significand.high = significand.high.add(Long.fromNumber(1));565    }566 567    // Biased exponent568    biasedExponent = exponent + EXPONENT_BIAS;569    const dec = { low: Long.fromNumber(0), high: Long.fromNumber(0) };570 571    // Encode combination, exponent, and significand.572    if (573      significand.high.shiftRightUnsigned(49).and(Long.fromNumber(1)).equals(Long.fromNumber(1))574    ) {575      // Encode '11' into bits 1 to 3576      dec.high = dec.high.or(Long.fromNumber(0x3).shiftLeft(61));577      dec.high = dec.high.or(578        Long.fromNumber(biasedExponent).and(Long.fromNumber(0x3fff).shiftLeft(47))579      );580      dec.high = dec.high.or(significand.high.and(Long.fromNumber(0x7fffffffffff)));581    } else {582      dec.high = dec.high.or(Long.fromNumber(biasedExponent & 0x3fff).shiftLeft(49));583      dec.high = dec.high.or(significand.high.and(Long.fromNumber(0x1ffffffffffff)));584    }585 586    dec.low = significand.low;587 588    // Encode sign589    if (isNegative) {590      dec.high = dec.high.or(Long.fromString('9223372036854775808'));591    }592 593    // Encode into a buffer594    const buffer = ByteUtils.allocateUnsafe(16);595    index = 0;596 597    // Encode the low 64 bits of the decimal598    // Encode low bits599    buffer[index++] = dec.low.low & 0xff;600    buffer[index++] = (dec.low.low >> 8) & 0xff;601    buffer[index++] = (dec.low.low >> 16) & 0xff;602    buffer[index++] = (dec.low.low >> 24) & 0xff;603    // Encode high bits604    buffer[index++] = dec.low.high & 0xff;605    buffer[index++] = (dec.low.high >> 8) & 0xff;606    buffer[index++] = (dec.low.high >> 16) & 0xff;607    buffer[index++] = (dec.low.high >> 24) & 0xff;608 609    // Encode the high 64 bits of the decimal610    // Encode low bits611    buffer[index++] = dec.high.low & 0xff;612    buffer[index++] = (dec.high.low >> 8) & 0xff;613    buffer[index++] = (dec.high.low >> 16) & 0xff;614    buffer[index++] = (dec.high.low >> 24) & 0xff;615    // Encode high bits616    buffer[index++] = dec.high.high & 0xff;617    buffer[index++] = (dec.high.high >> 8) & 0xff;618    buffer[index++] = (dec.high.high >> 16) & 0xff;619    buffer[index++] = (dec.high.high >> 24) & 0xff;620 621    // Return the new Decimal128622    return new Decimal128(buffer);623  }624  /** Create a string representation of the raw Decimal128 value */625  toString(): string {626    // Note: bits in this routine are referred to starting at 0,627    // from the sign bit, towards the coefficient.628 629    // decoded biased exponent (14 bits)630    let biased_exponent;631    // the number of significand digits632    let significand_digits = 0;633    // the base-10 digits in the significand634    const significand = new Array<number>(36);635    for (let i = 0; i < significand.length; i++) significand[i] = 0;636    // read pointer into significand637    let index = 0;638 639    // true if the number is zero640    let is_zero = false;641 642    // the most significant significand bits (50-46)643    let significand_msb;644    // temporary storage for significand decoding645    let significand128: { parts: [number, number, number, number] } = { parts: [0, 0, 0, 0] };646    // indexing variables647    let j, k;648 649    // Output string650    const string: string[] = [];651 652    // Unpack index653    index = 0;654 655    // Buffer reference656    const buffer = this.bytes;657 658    // Unpack the low 64bits into a long659    // bits 96 - 127660    const low =661      buffer[index++] | (buffer[index++] << 8) | (buffer[index++] << 16) | (buffer[index++] << 24);662    // bits 64 - 95663    const midl =664      buffer[index++] | (buffer[index++] << 8) | (buffer[index++] << 16) | (buffer[index++] << 24);665 666    // Unpack the high 64bits into a long667    // bits 32 - 63668    const midh =669      buffer[index++] | (buffer[index++] << 8) | (buffer[index++] << 16) | (buffer[index++] << 24);670    // bits 0 - 31671    const high =672      buffer[index++] | (buffer[index++] << 8) | (buffer[index++] << 16) | (buffer[index++] << 24);673 674    // Unpack index675    index = 0;676 677    // Create the state of the decimal678    const dec = {679      low: new Long(low, midl),680      high: new Long(midh, high)681    };682 683    if (dec.high.lessThan(Long.ZERO)) {684      string.push('-');685    }686 687    // Decode combination field and exponent688    // bits 1 - 5689    const combination = (high >> 26) & COMBINATION_MASK;690 691    if (combination >> 3 === 3) {692      // Check for 'special' values693      if (combination === COMBINATION_INFINITY) {694        return string.join('') + 'Infinity';695      } else if (combination === COMBINATION_NAN) {696        return 'NaN';697      } else {698        biased_exponent = (high >> 15) & EXPONENT_MASK;699        significand_msb = 0x08 + ((high >> 14) & 0x01);700      }701    } else {702      significand_msb = (high >> 14) & 0x07;703      biased_exponent = (high >> 17) & EXPONENT_MASK;704    }705 706    // unbiased exponent707    const exponent = biased_exponent - EXPONENT_BIAS;708 709    // Create string of significand digits710 711    // Convert the 114-bit binary number represented by712    // (significand_high, significand_low) to at most 34 decimal713    // digits through modulo and division.714    significand128.parts[0] = (high & 0x3fff) + ((significand_msb & 0xf) << 14);715    significand128.parts[1] = midh;716    significand128.parts[2] = midl;717    significand128.parts[3] = low;718 719    if (720      significand128.parts[0] === 0 &&721      significand128.parts[1] === 0 &&722      significand128.parts[2] === 0 &&723      significand128.parts[3] === 0724    ) {725      is_zero = true;726    } else {727      for (k = 3; k >= 0; k--) {728        let least_digits = 0;729        // Perform the divide730        const result = divideu128(significand128);731        significand128 = result.quotient;732        least_digits = result.rem.low;733 734        // We now have the 9 least significant digits (in base 2).735        // Convert and output to string.736        if (!least_digits) continue;737 738        for (j = 8; j >= 0; j--) {739          // significand[k * 9 + j] = Math.round(least_digits % 10);740          significand[k * 9 + j] = least_digits % 10;741          // least_digits = Math.round(least_digits / 10);742          least_digits = Math.floor(least_digits / 10);743        }744      }745    }746 747    // Output format options:748    // Scientific - [-]d.dddE(+/-)dd or [-]dE(+/-)dd749    // Regular    - ddd.ddd750 751    if (is_zero) {752      significand_digits = 1;753      significand[index] = 0;754    } else {755      significand_digits = 36;756      while (!significand[index]) {757        significand_digits = significand_digits - 1;758        index = index + 1;759      }760    }761 762    // the exponent if scientific notation is used763    const scientific_exponent = significand_digits - 1 + exponent;764 765    // The scientific exponent checks are dictated by the string conversion766    // specification and are somewhat arbitrary cutoffs.767    //768    // We must check exponent > 0, because if this is the case, the number769    // has trailing zeros.  However, we *cannot* output these trailing zeros,770    // because doing so would change the precision of the value, and would771    // change stored data if the string converted number is round tripped.772    if (scientific_exponent >= 34 || scientific_exponent <= -7 || exponent > 0) {773      // Scientific format774 775      // if there are too many significant digits, we should just be treating numbers776      // as + or - 0 and using the non-scientific exponent (this is for the "invalid777      // representation should be treated as 0/-0" spec cases in decimal128-1.json)778      if (significand_digits > 34) {779        string.push(`${0}`);780        if (exponent > 0) string.push(`E+${exponent}`);781        else if (exponent < 0) string.push(`E${exponent}`);782        return string.join('');783      }784 785      string.push(`${significand[index++]}`);786      significand_digits = significand_digits - 1;787 788      if (significand_digits) {789        string.push('.');790      }791 792      for (let i = 0; i < significand_digits; i++) {793        string.push(`${significand[index++]}`);794      }795 796      // Exponent797      string.push('E');798      if (scientific_exponent > 0) {799        string.push(`+${scientific_exponent}`);800      } else {801        string.push(`${scientific_exponent}`);802      }803    } else {804      // Regular format with no decimal place805      if (exponent >= 0) {806        for (let i = 0; i < significand_digits; i++) {807          string.push(`${significand[index++]}`);808        }809      } else {810        let radix_position = significand_digits + exponent;811 812        // non-zero digits before radix813        if (radix_position > 0) {814          for (let i = 0; i < radix_position; i++) {815            string.push(`${significand[index++]}`);816          }817        } else {818          string.push('0');819        }820 821        string.push('.');822        // add leading zeros after radix823        while (radix_position++ < 0) {824          string.push('0');825        }826 827        for (let i = 0; i < significand_digits - Math.max(radix_position - 1, 0); i++) {828          string.push(`${significand[index++]}`);829        }830      }831    }832 833    return string.join('');834  }835 836  toJSON(): Decimal128Extended {837    return { $numberDecimal: this.toString() };838  }839 840  /** @internal */841  toExtendedJSON(): Decimal128Extended {842    return { $numberDecimal: this.toString() };843  }844 845  /** @internal */846  static fromExtendedJSON(doc: Decimal128Extended): Decimal128 {847    return Decimal128.fromString(doc.$numberDecimal);848  }849 850  inspect(depth?: number, options?: unknown, inspect?: InspectFn): string {851    inspect ??= defaultInspect;852    const d128string = inspect(this.toString(), options);853    return `new Decimal128(${d128string})`;854  }855}856