|
|
import { BSONValue } from './bson_value'; |
|
|
import { BSONError } from './error'; |
|
|
import { Long } from './long'; |
|
|
import { isUint8Array } from './parser/utils'; |
|
|
import { ByteUtils } from './utils/byte_utils'; |
|
|
|
|
|
const PARSE_STRING_REGEXP = /^(\+|-)?(\d+|(\d*\.\d*))?(E|e)?([-+])?(\d+)?$/; |
|
|
const PARSE_INF_REGEXP = /^(\+|-)?(Infinity|inf)$/i; |
|
|
const PARSE_NAN_REGEXP = /^(\+|-)?NaN$/i; |
|
|
|
|
|
const EXPONENT_MAX = 6111; |
|
|
const EXPONENT_MIN = -6176; |
|
|
const EXPONENT_BIAS = 6176; |
|
|
const MAX_DIGITS = 34; |
|
|
|
|
|
|
|
|
const NAN_BUFFER = ByteUtils.fromNumberArray( |
|
|
[ |
|
|
0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
|
|
].reverse() |
|
|
); |
|
|
|
|
|
const INF_NEGATIVE_BUFFER = ByteUtils.fromNumberArray( |
|
|
[ |
|
|
0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
|
|
].reverse() |
|
|
); |
|
|
const INF_POSITIVE_BUFFER = ByteUtils.fromNumberArray( |
|
|
[ |
|
|
0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
|
|
].reverse() |
|
|
); |
|
|
|
|
|
const EXPONENT_REGEX = /^([-+])?(\d+)?$/; |
|
|
|
|
|
|
|
|
const COMBINATION_MASK = 0x1f; |
|
|
|
|
|
const EXPONENT_MASK = 0x3fff; |
|
|
|
|
|
const COMBINATION_INFINITY = 30; |
|
|
|
|
|
const COMBINATION_NAN = 31; |
|
|
|
|
|
|
|
|
function isDigit(value: string): boolean { |
|
|
return !isNaN(parseInt(value, 10)); |
|
|
} |
|
|
|
|
|
|
|
|
function divideu128(value: { parts: [number, number, number, number] }) { |
|
|
const DIVISOR = Long.fromNumber(1000 * 1000 * 1000); |
|
|
let _rem = Long.fromNumber(0); |
|
|
|
|
|
if (!value.parts[0] && !value.parts[1] && !value.parts[2] && !value.parts[3]) { |
|
|
return { quotient: value, rem: _rem }; |
|
|
} |
|
|
|
|
|
for (let i = 0; i <= 3; i++) { |
|
|
|
|
|
_rem = _rem.shiftLeft(32); |
|
|
|
|
|
_rem = _rem.add(new Long(value.parts[i], 0)); |
|
|
value.parts[i] = _rem.div(DIVISOR).low; |
|
|
_rem = _rem.modulo(DIVISOR); |
|
|
} |
|
|
|
|
|
return { quotient: value, rem: _rem }; |
|
|
} |
|
|
|
|
|
|
|
|
function multiply64x2(left: Long, right: Long): { high: Long; low: Long } { |
|
|
if (!left && !right) { |
|
|
return { high: Long.fromNumber(0), low: Long.fromNumber(0) }; |
|
|
} |
|
|
|
|
|
const leftHigh = left.shiftRightUnsigned(32); |
|
|
const leftLow = new Long(left.getLowBits(), 0); |
|
|
const rightHigh = right.shiftRightUnsigned(32); |
|
|
const rightLow = new Long(right.getLowBits(), 0); |
|
|
|
|
|
let productHigh = leftHigh.multiply(rightHigh); |
|
|
let productMid = leftHigh.multiply(rightLow); |
|
|
const productMid2 = leftLow.multiply(rightHigh); |
|
|
let productLow = leftLow.multiply(rightLow); |
|
|
|
|
|
productHigh = productHigh.add(productMid.shiftRightUnsigned(32)); |
|
|
productMid = new Long(productMid.getLowBits(), 0) |
|
|
.add(productMid2) |
|
|
.add(productLow.shiftRightUnsigned(32)); |
|
|
|
|
|
productHigh = productHigh.add(productMid.shiftRightUnsigned(32)); |
|
|
productLow = productMid.shiftLeft(32).add(new Long(productLow.getLowBits(), 0)); |
|
|
|
|
|
|
|
|
return { high: productHigh, low: productLow }; |
|
|
} |
|
|
|
|
|
function lessThan(left: Long, right: Long): boolean { |
|
|
|
|
|
const uhleft = left.high >>> 0; |
|
|
const uhright = right.high >>> 0; |
|
|
|
|
|
|
|
|
if (uhleft < uhright) { |
|
|
return true; |
|
|
} else if (uhleft === uhright) { |
|
|
const ulleft = left.low >>> 0; |
|
|
const ulright = right.low >>> 0; |
|
|
if (ulleft < ulright) return true; |
|
|
} |
|
|
|
|
|
return false; |
|
|
} |
|
|
|
|
|
function invalidErr(string: string, message: string) { |
|
|
throw new BSONError(`"${string}" is not a valid Decimal128 string - ${message}`); |
|
|
} |
|
|
|
|
|
|
|
|
export interface Decimal128Extended { |
|
|
$numberDecimal: string; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export class Decimal128 extends BSONValue { |
|
|
get _bsontype(): 'Decimal128' { |
|
|
return 'Decimal128'; |
|
|
} |
|
|
|
|
|
readonly bytes!: Uint8Array; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor(bytes: Uint8Array | string) { |
|
|
super(); |
|
|
if (typeof bytes === 'string') { |
|
|
this.bytes = Decimal128.fromString(bytes).bytes; |
|
|
} else if (isUint8Array(bytes)) { |
|
|
if (bytes.byteLength !== 16) { |
|
|
throw new BSONError('Decimal128 must take a Buffer of 16 bytes'); |
|
|
} |
|
|
this.bytes = bytes; |
|
|
} else { |
|
|
throw new BSONError('Decimal128 must take a Buffer or string'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static fromString(representation: string): Decimal128 { |
|
|
return Decimal128._fromString(representation, { allowRounding: false }); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static fromStringWithRounding(representation: string): Decimal128 { |
|
|
return Decimal128._fromString(representation, { allowRounding: true }); |
|
|
} |
|
|
|
|
|
private static _fromString(representation: string, options: { allowRounding: boolean }) { |
|
|
|
|
|
let isNegative = false; |
|
|
let sawSign = false; |
|
|
let sawRadix = false; |
|
|
let foundNonZero = false; |
|
|
|
|
|
|
|
|
let significantDigits = 0; |
|
|
|
|
|
let nDigitsRead = 0; |
|
|
|
|
|
let nDigits = 0; |
|
|
|
|
|
let radixPosition = 0; |
|
|
|
|
|
let firstNonZero = 0; |
|
|
|
|
|
|
|
|
const digits = [0]; |
|
|
|
|
|
let nDigitsStored = 0; |
|
|
|
|
|
let digitsInsert = 0; |
|
|
|
|
|
let lastDigit = 0; |
|
|
|
|
|
|
|
|
let exponent = 0; |
|
|
|
|
|
let significandHigh = new Long(0, 0); |
|
|
|
|
|
let significandLow = new Long(0, 0); |
|
|
|
|
|
let biasedExponent = 0; |
|
|
|
|
|
|
|
|
let index = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (representation.length >= 7000) { |
|
|
throw new BSONError('' + representation + ' not a valid Decimal128 string'); |
|
|
} |
|
|
|
|
|
|
|
|
const stringMatch = representation.match(PARSE_STRING_REGEXP); |
|
|
const infMatch = representation.match(PARSE_INF_REGEXP); |
|
|
const nanMatch = representation.match(PARSE_NAN_REGEXP); |
|
|
|
|
|
|
|
|
if ((!stringMatch && !infMatch && !nanMatch) || representation.length === 0) { |
|
|
throw new BSONError('' + representation + ' not a valid Decimal128 string'); |
|
|
} |
|
|
|
|
|
if (stringMatch) { |
|
|
|
|
|
|
|
|
|
|
|
const unsignedNumber = stringMatch[2]; |
|
|
|
|
|
|
|
|
|
|
|
const e = stringMatch[4]; |
|
|
const expSign = stringMatch[5]; |
|
|
const expNumber = stringMatch[6]; |
|
|
|
|
|
|
|
|
if (e && expNumber === undefined) invalidErr(representation, 'missing exponent power'); |
|
|
|
|
|
|
|
|
if (e && unsignedNumber === undefined) invalidErr(representation, 'missing exponent base'); |
|
|
|
|
|
if (e === undefined && (expSign || expNumber)) { |
|
|
invalidErr(representation, 'missing e before exponent'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (representation[index] === '+' || representation[index] === '-') { |
|
|
sawSign = true; |
|
|
isNegative = representation[index++] === '-'; |
|
|
} |
|
|
|
|
|
|
|
|
if (!isDigit(representation[index]) && representation[index] !== '.') { |
|
|
if (representation[index] === 'i' || representation[index] === 'I') { |
|
|
return new Decimal128(isNegative ? INF_NEGATIVE_BUFFER : INF_POSITIVE_BUFFER); |
|
|
} else if (representation[index] === 'N') { |
|
|
return new Decimal128(NAN_BUFFER); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
while (isDigit(representation[index]) || representation[index] === '.') { |
|
|
if (representation[index] === '.') { |
|
|
if (sawRadix) invalidErr(representation, 'contains multiple periods'); |
|
|
|
|
|
sawRadix = true; |
|
|
index = index + 1; |
|
|
continue; |
|
|
} |
|
|
|
|
|
if (nDigitsStored < MAX_DIGITS) { |
|
|
if (representation[index] !== '0' || foundNonZero) { |
|
|
if (!foundNonZero) { |
|
|
firstNonZero = nDigitsRead; |
|
|
} |
|
|
|
|
|
foundNonZero = true; |
|
|
|
|
|
|
|
|
digits[digitsInsert++] = parseInt(representation[index], 10); |
|
|
nDigitsStored = nDigitsStored + 1; |
|
|
} |
|
|
} |
|
|
|
|
|
if (foundNonZero) nDigits = nDigits + 1; |
|
|
if (sawRadix) radixPosition = radixPosition + 1; |
|
|
|
|
|
nDigitsRead = nDigitsRead + 1; |
|
|
index = index + 1; |
|
|
} |
|
|
|
|
|
if (sawRadix && !nDigitsRead) |
|
|
throw new BSONError('' + representation + ' not a valid Decimal128 string'); |
|
|
|
|
|
|
|
|
if (representation[index] === 'e' || representation[index] === 'E') { |
|
|
|
|
|
const match = representation.substr(++index).match(EXPONENT_REGEX); |
|
|
|
|
|
|
|
|
if (!match || !match[2]) return new Decimal128(NAN_BUFFER); |
|
|
|
|
|
|
|
|
exponent = parseInt(match[0], 10); |
|
|
|
|
|
|
|
|
index = index + match[0].length; |
|
|
} |
|
|
|
|
|
|
|
|
if (representation[index]) return new Decimal128(NAN_BUFFER); |
|
|
|
|
|
|
|
|
|
|
|
if (!nDigitsStored) { |
|
|
digits[0] = 0; |
|
|
nDigits = 1; |
|
|
nDigitsStored = 1; |
|
|
significantDigits = 0; |
|
|
} else { |
|
|
lastDigit = nDigitsStored - 1; |
|
|
significantDigits = nDigits; |
|
|
if (significantDigits !== 1) { |
|
|
while ( |
|
|
representation[ |
|
|
firstNonZero + significantDigits - 1 + Number(sawSign) + Number(sawRadix) |
|
|
] === '0' |
|
|
) { |
|
|
significantDigits = significantDigits - 1; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (exponent <= radixPosition && radixPosition > exponent + (1 << 14)) { |
|
|
exponent = EXPONENT_MIN; |
|
|
} else { |
|
|
exponent = exponent - radixPosition; |
|
|
} |
|
|
|
|
|
|
|
|
while (exponent > EXPONENT_MAX) { |
|
|
|
|
|
lastDigit = lastDigit + 1; |
|
|
if (lastDigit >= MAX_DIGITS) { |
|
|
|
|
|
if (significantDigits === 0) { |
|
|
exponent = EXPONENT_MAX; |
|
|
break; |
|
|
} |
|
|
|
|
|
invalidErr(representation, 'overflow'); |
|
|
} |
|
|
exponent = exponent - 1; |
|
|
} |
|
|
|
|
|
if (options.allowRounding) { |
|
|
while (exponent < EXPONENT_MIN || nDigitsStored < nDigits) { |
|
|
|
|
|
if (lastDigit === 0 && significantDigits < nDigitsStored) { |
|
|
exponent = EXPONENT_MIN; |
|
|
significantDigits = 0; |
|
|
break; |
|
|
} |
|
|
|
|
|
if (nDigitsStored < nDigits) { |
|
|
|
|
|
nDigits = nDigits - 1; |
|
|
} else { |
|
|
|
|
|
lastDigit = lastDigit - 1; |
|
|
} |
|
|
|
|
|
if (exponent < EXPONENT_MAX) { |
|
|
exponent = exponent + 1; |
|
|
} else { |
|
|
|
|
|
const digitsString = digits.join(''); |
|
|
if (digitsString.match(/^0+$/)) { |
|
|
exponent = EXPONENT_MAX; |
|
|
break; |
|
|
} |
|
|
invalidErr(representation, 'overflow'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (lastDigit + 1 < significantDigits) { |
|
|
let endOfString = nDigitsRead; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (sawRadix) { |
|
|
firstNonZero = firstNonZero + 1; |
|
|
endOfString = endOfString + 1; |
|
|
} |
|
|
|
|
|
if (sawSign) { |
|
|
firstNonZero = firstNonZero + 1; |
|
|
endOfString = endOfString + 1; |
|
|
} |
|
|
|
|
|
const roundDigit = parseInt(representation[firstNonZero + lastDigit + 1], 10); |
|
|
let roundBit = 0; |
|
|
|
|
|
if (roundDigit >= 5) { |
|
|
roundBit = 1; |
|
|
if (roundDigit === 5) { |
|
|
roundBit = digits[lastDigit] % 2 === 1 ? 1 : 0; |
|
|
for (let i = firstNonZero + lastDigit + 2; i < endOfString; i++) { |
|
|
if (parseInt(representation[i], 10)) { |
|
|
roundBit = 1; |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if (roundBit) { |
|
|
let dIdx = lastDigit; |
|
|
|
|
|
for (; dIdx >= 0; dIdx--) { |
|
|
if (++digits[dIdx] > 9) { |
|
|
digits[dIdx] = 0; |
|
|
|
|
|
|
|
|
if (dIdx === 0) { |
|
|
if (exponent < EXPONENT_MAX) { |
|
|
exponent = exponent + 1; |
|
|
digits[dIdx] = 1; |
|
|
} else { |
|
|
return new Decimal128(isNegative ? INF_NEGATIVE_BUFFER : INF_POSITIVE_BUFFER); |
|
|
} |
|
|
} |
|
|
} else { |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} else { |
|
|
while (exponent < EXPONENT_MIN || nDigitsStored < nDigits) { |
|
|
|
|
|
if (lastDigit === 0) { |
|
|
if (significantDigits === 0) { |
|
|
exponent = EXPONENT_MIN; |
|
|
break; |
|
|
} |
|
|
|
|
|
invalidErr(representation, 'exponent underflow'); |
|
|
} |
|
|
|
|
|
if (nDigitsStored < nDigits) { |
|
|
if ( |
|
|
representation[nDigits - 1 + Number(sawSign) + Number(sawRadix)] !== '0' && |
|
|
significantDigits !== 0 |
|
|
) { |
|
|
invalidErr(representation, 'inexact rounding'); |
|
|
} |
|
|
|
|
|
nDigits = nDigits - 1; |
|
|
} else { |
|
|
if (digits[lastDigit] !== 0) { |
|
|
invalidErr(representation, 'inexact rounding'); |
|
|
} |
|
|
|
|
|
lastDigit = lastDigit - 1; |
|
|
} |
|
|
|
|
|
if (exponent < EXPONENT_MAX) { |
|
|
exponent = exponent + 1; |
|
|
} else { |
|
|
invalidErr(representation, 'overflow'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (lastDigit + 1 < significantDigits) { |
|
|
|
|
|
|
|
|
|
|
|
if (sawRadix) { |
|
|
firstNonZero = firstNonZero + 1; |
|
|
} |
|
|
|
|
|
if (sawSign) { |
|
|
firstNonZero = firstNonZero + 1; |
|
|
} |
|
|
|
|
|
const roundDigit = parseInt(representation[firstNonZero + lastDigit + 1], 10); |
|
|
|
|
|
if (roundDigit !== 0) { |
|
|
invalidErr(representation, 'inexact rounding'); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
significandHigh = Long.fromNumber(0); |
|
|
|
|
|
significandLow = Long.fromNumber(0); |
|
|
|
|
|
|
|
|
if (significantDigits === 0) { |
|
|
significandHigh = Long.fromNumber(0); |
|
|
significandLow = Long.fromNumber(0); |
|
|
} else if (lastDigit < 17) { |
|
|
let dIdx = 0; |
|
|
significandLow = Long.fromNumber(digits[dIdx++]); |
|
|
significandHigh = new Long(0, 0); |
|
|
|
|
|
for (; dIdx <= lastDigit; dIdx++) { |
|
|
significandLow = significandLow.multiply(Long.fromNumber(10)); |
|
|
significandLow = significandLow.add(Long.fromNumber(digits[dIdx])); |
|
|
} |
|
|
} else { |
|
|
let dIdx = 0; |
|
|
significandHigh = Long.fromNumber(digits[dIdx++]); |
|
|
|
|
|
for (; dIdx <= lastDigit - 17; dIdx++) { |
|
|
significandHigh = significandHigh.multiply(Long.fromNumber(10)); |
|
|
significandHigh = significandHigh.add(Long.fromNumber(digits[dIdx])); |
|
|
} |
|
|
|
|
|
significandLow = Long.fromNumber(digits[dIdx++]); |
|
|
|
|
|
for (; dIdx <= lastDigit; dIdx++) { |
|
|
significandLow = significandLow.multiply(Long.fromNumber(10)); |
|
|
significandLow = significandLow.add(Long.fromNumber(digits[dIdx])); |
|
|
} |
|
|
} |
|
|
|
|
|
const significand = multiply64x2(significandHigh, Long.fromString('100000000000000000')); |
|
|
significand.low = significand.low.add(significandLow); |
|
|
|
|
|
if (lessThan(significand.low, significandLow)) { |
|
|
significand.high = significand.high.add(Long.fromNumber(1)); |
|
|
} |
|
|
|
|
|
|
|
|
biasedExponent = exponent + EXPONENT_BIAS; |
|
|
const dec = { low: Long.fromNumber(0), high: Long.fromNumber(0) }; |
|
|
|
|
|
|
|
|
if ( |
|
|
significand.high.shiftRightUnsigned(49).and(Long.fromNumber(1)).equals(Long.fromNumber(1)) |
|
|
) { |
|
|
|
|
|
dec.high = dec.high.or(Long.fromNumber(0x3).shiftLeft(61)); |
|
|
dec.high = dec.high.or( |
|
|
Long.fromNumber(biasedExponent).and(Long.fromNumber(0x3fff).shiftLeft(47)) |
|
|
); |
|
|
dec.high = dec.high.or(significand.high.and(Long.fromNumber(0x7fffffffffff))); |
|
|
} else { |
|
|
dec.high = dec.high.or(Long.fromNumber(biasedExponent & 0x3fff).shiftLeft(49)); |
|
|
dec.high = dec.high.or(significand.high.and(Long.fromNumber(0x1ffffffffffff))); |
|
|
} |
|
|
|
|
|
dec.low = significand.low; |
|
|
|
|
|
|
|
|
if (isNegative) { |
|
|
dec.high = dec.high.or(Long.fromString('9223372036854775808')); |
|
|
} |
|
|
|
|
|
|
|
|
const buffer = ByteUtils.allocate(16); |
|
|
index = 0; |
|
|
|
|
|
|
|
|
|
|
|
buffer[index++] = dec.low.low & 0xff; |
|
|
buffer[index++] = (dec.low.low >> 8) & 0xff; |
|
|
buffer[index++] = (dec.low.low >> 16) & 0xff; |
|
|
buffer[index++] = (dec.low.low >> 24) & 0xff; |
|
|
|
|
|
buffer[index++] = dec.low.high & 0xff; |
|
|
buffer[index++] = (dec.low.high >> 8) & 0xff; |
|
|
buffer[index++] = (dec.low.high >> 16) & 0xff; |
|
|
buffer[index++] = (dec.low.high >> 24) & 0xff; |
|
|
|
|
|
|
|
|
|
|
|
buffer[index++] = dec.high.low & 0xff; |
|
|
buffer[index++] = (dec.high.low >> 8) & 0xff; |
|
|
buffer[index++] = (dec.high.low >> 16) & 0xff; |
|
|
buffer[index++] = (dec.high.low >> 24) & 0xff; |
|
|
|
|
|
buffer[index++] = dec.high.high & 0xff; |
|
|
buffer[index++] = (dec.high.high >> 8) & 0xff; |
|
|
buffer[index++] = (dec.high.high >> 16) & 0xff; |
|
|
buffer[index++] = (dec.high.high >> 24) & 0xff; |
|
|
|
|
|
|
|
|
return new Decimal128(buffer); |
|
|
} |
|
|
|
|
|
toString(): string { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let biased_exponent; |
|
|
|
|
|
let significand_digits = 0; |
|
|
|
|
|
const significand = new Array<number>(36); |
|
|
for (let i = 0; i < significand.length; i++) significand[i] = 0; |
|
|
|
|
|
let index = 0; |
|
|
|
|
|
|
|
|
let is_zero = false; |
|
|
|
|
|
|
|
|
let significand_msb; |
|
|
|
|
|
let significand128: { parts: [number, number, number, number] } = { parts: [0, 0, 0, 0] }; |
|
|
|
|
|
let j, k; |
|
|
|
|
|
|
|
|
const string: string[] = []; |
|
|
|
|
|
|
|
|
index = 0; |
|
|
|
|
|
|
|
|
const buffer = this.bytes; |
|
|
|
|
|
|
|
|
|
|
|
const low = |
|
|
buffer[index++] | (buffer[index++] << 8) | (buffer[index++] << 16) | (buffer[index++] << 24); |
|
|
|
|
|
const midl = |
|
|
buffer[index++] | (buffer[index++] << 8) | (buffer[index++] << 16) | (buffer[index++] << 24); |
|
|
|
|
|
|
|
|
|
|
|
const midh = |
|
|
buffer[index++] | (buffer[index++] << 8) | (buffer[index++] << 16) | (buffer[index++] << 24); |
|
|
|
|
|
const high = |
|
|
buffer[index++] | (buffer[index++] << 8) | (buffer[index++] << 16) | (buffer[index++] << 24); |
|
|
|
|
|
|
|
|
index = 0; |
|
|
|
|
|
|
|
|
const dec = { |
|
|
low: new Long(low, midl), |
|
|
high: new Long(midh, high) |
|
|
}; |
|
|
|
|
|
if (dec.high.lessThan(Long.ZERO)) { |
|
|
string.push('-'); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const combination = (high >> 26) & COMBINATION_MASK; |
|
|
|
|
|
if (combination >> 3 === 3) { |
|
|
|
|
|
if (combination === COMBINATION_INFINITY) { |
|
|
return string.join('') + 'Infinity'; |
|
|
} else if (combination === COMBINATION_NAN) { |
|
|
return 'NaN'; |
|
|
} else { |
|
|
biased_exponent = (high >> 15) & EXPONENT_MASK; |
|
|
significand_msb = 0x08 + ((high >> 14) & 0x01); |
|
|
} |
|
|
} else { |
|
|
significand_msb = (high >> 14) & 0x07; |
|
|
biased_exponent = (high >> 17) & EXPONENT_MASK; |
|
|
} |
|
|
|
|
|
|
|
|
const exponent = biased_exponent - EXPONENT_BIAS; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
significand128.parts[0] = (high & 0x3fff) + ((significand_msb & 0xf) << 14); |
|
|
significand128.parts[1] = midh; |
|
|
significand128.parts[2] = midl; |
|
|
significand128.parts[3] = low; |
|
|
|
|
|
if ( |
|
|
significand128.parts[0] === 0 && |
|
|
significand128.parts[1] === 0 && |
|
|
significand128.parts[2] === 0 && |
|
|
significand128.parts[3] === 0 |
|
|
) { |
|
|
is_zero = true; |
|
|
} else { |
|
|
for (k = 3; k >= 0; k--) { |
|
|
let least_digits = 0; |
|
|
|
|
|
const result = divideu128(significand128); |
|
|
significand128 = result.quotient; |
|
|
least_digits = result.rem.low; |
|
|
|
|
|
|
|
|
|
|
|
if (!least_digits) continue; |
|
|
|
|
|
for (j = 8; j >= 0; j--) { |
|
|
|
|
|
significand[k * 9 + j] = least_digits % 10; |
|
|
|
|
|
least_digits = Math.floor(least_digits / 10); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (is_zero) { |
|
|
significand_digits = 1; |
|
|
significand[index] = 0; |
|
|
} else { |
|
|
significand_digits = 36; |
|
|
while (!significand[index]) { |
|
|
significand_digits = significand_digits - 1; |
|
|
index = index + 1; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const scientific_exponent = significand_digits - 1 + exponent; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (scientific_exponent >= 34 || scientific_exponent <= -7 || exponent > 0) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (significand_digits > 34) { |
|
|
string.push(`${0}`); |
|
|
if (exponent > 0) string.push(`E+${exponent}`); |
|
|
else if (exponent < 0) string.push(`E${exponent}`); |
|
|
return string.join(''); |
|
|
} |
|
|
|
|
|
string.push(`${significand[index++]}`); |
|
|
significand_digits = significand_digits - 1; |
|
|
|
|
|
if (significand_digits) { |
|
|
string.push('.'); |
|
|
} |
|
|
|
|
|
for (let i = 0; i < significand_digits; i++) { |
|
|
string.push(`${significand[index++]}`); |
|
|
} |
|
|
|
|
|
|
|
|
string.push('E'); |
|
|
if (scientific_exponent > 0) { |
|
|
string.push(`+${scientific_exponent}`); |
|
|
} else { |
|
|
string.push(`${scientific_exponent}`); |
|
|
} |
|
|
} else { |
|
|
|
|
|
if (exponent >= 0) { |
|
|
for (let i = 0; i < significand_digits; i++) { |
|
|
string.push(`${significand[index++]}`); |
|
|
} |
|
|
} else { |
|
|
let radix_position = significand_digits + exponent; |
|
|
|
|
|
|
|
|
if (radix_position > 0) { |
|
|
for (let i = 0; i < radix_position; i++) { |
|
|
string.push(`${significand[index++]}`); |
|
|
} |
|
|
} else { |
|
|
string.push('0'); |
|
|
} |
|
|
|
|
|
string.push('.'); |
|
|
|
|
|
while (radix_position++ < 0) { |
|
|
string.push('0'); |
|
|
} |
|
|
|
|
|
for (let i = 0; i < significand_digits - Math.max(radix_position - 1, 0); i++) { |
|
|
string.push(`${significand[index++]}`); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return string.join(''); |
|
|
} |
|
|
|
|
|
toJSON(): Decimal128Extended { |
|
|
return { $numberDecimal: this.toString() }; |
|
|
} |
|
|
|
|
|
|
|
|
toExtendedJSON(): Decimal128Extended { |
|
|
return { $numberDecimal: this.toString() }; |
|
|
} |
|
|
|
|
|
|
|
|
static fromExtendedJSON(doc: Decimal128Extended): Decimal128 { |
|
|
return Decimal128.fromString(doc.$numberDecimal); |
|
|
} |
|
|
|
|
|
|
|
|
[Symbol.for('nodejs.util.inspect.custom')](): string { |
|
|
return this.inspect(); |
|
|
} |
|
|
|
|
|
inspect(): string { |
|
|
return `new Decimal128("${this.toString()}")`; |
|
|
} |
|
|
} |
|
|
|