UNPKG

@node-dlc/messaging

Version:
294 lines 8.58 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.F64 = void 0; const decimal_js_1 = __importDefault(require("decimal.js")); /** * IEEE 754 double-precision floating-point number implementation * * Format: 1 bit sign + 11 bits exponent + 52 bits mantissa * * This class works directly with the binary representation to avoid * precision loss when serializing/deserializing f64 values to/from buffers. */ class F64 { /** * Create an F64 from raw bytes (IEEE 754 format) */ constructor(buffer) { if (buffer) { if (buffer.length !== 8) { throw new Error('F64 buffer must be exactly 8 bytes'); } this._buffer = Buffer.from(buffer); } else { this._buffer = Buffer.alloc(8); } } /** * Create F64 from JavaScript number */ static fromNumber(num) { const buffer = Buffer.alloc(8); buffer.writeDoubleBE(num, 0); return new F64(buffer); } /** * Create F64 from Decimal.js value * Uses the most precise conversion possible */ static fromDecimal(decimal) { // Convert to number and let IEEE 754 handle the precision const num = decimal.toNumber(); return F64.fromNumber(num); } /** * Create F64 from string representation * Uses Decimal.js to parse the string without precision loss from JavaScript Number */ static fromString(str) { // Parse string using Decimal.js to avoid JavaScript Number precision loss const decimal = new decimal_js_1.default(str); return F64.fromDecimal(decimal); } /** * Create F64 from bigint (treating as integer value) */ static fromBigInt(bigint) { const num = Number(bigint); return F64.fromNumber(num); } /** * Create F64 representing zero (static method for API compatibility) */ static zero() { return F64.fromNumber(0); } /** * Create F64 with specific IEEE 754 components */ static fromComponents(sign, exponent, mantissa) { if (exponent < 0 || exponent > 2047) { throw new Error('Exponent must be between 0 and 2047'); } if (mantissa < BigInt(0) || mantissa >= BigInt(1) << BigInt(52)) { throw new Error('Mantissa must be between 0 and 2^52-1'); } const buffer = Buffer.alloc(8); // Pack into 64-bit big-endian format // Bit layout: SEEEEEEE EEEEMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM const signBit = sign ? BigInt(1) : BigInt(0); const expBits = BigInt(exponent); const mantissaBits = mantissa; const ieee754Bits = (signBit << BigInt(63)) | (expBits << BigInt(52)) | mantissaBits; // Write as big-endian bytes for (let i = 0; i < 8; i++) { const byteValue = Number((ieee754Bits >> BigInt(56 - i * 8)) & BigInt(0xff)); buffer.writeUInt8(byteValue, i); } return new F64(buffer); } /** * Get the raw buffer containing IEEE 754 representation */ getBuffer() { return Buffer.from(this._buffer); } /** * Serialize to buffer (big-endian) */ serialize() { return this.getBuffer(); } /** * Deserialize from buffer (big-endian) */ static deserialize(buffer) { return new F64(buffer); } /** * Convert to JavaScript number * This may lose precision for very large or very precise values */ toNumber() { return this._buffer.readDoubleBE(0); } /** * Convert to Decimal.js for arbitrary precision */ toDecimal() { return new decimal_js_1.default(this.toNumber()); } /** * Extract IEEE 754 components */ getComponents() { // Read as big-endian 64-bit integer let ieee754Bits = BigInt(0); for (let i = 0; i < 8; i++) { ieee754Bits = (ieee754Bits << BigInt(8)) | BigInt(this._buffer.readUInt8(i)); } const sign = (ieee754Bits & (BigInt(1) << BigInt(63))) !== BigInt(0); const exponent = Number((ieee754Bits >> BigInt(52)) & BigInt(0x7ff)); const mantissa = ieee754Bits & ((BigInt(1) << BigInt(52)) - BigInt(1)); return { sign, exponent, mantissa }; } /** * Check if the value is finite */ isFinite() { const { exponent } = this.getComponents(); return exponent !== 2047; // NaN and Infinity have exponent = 2047 } /** * Check if the value is NaN */ isNaN() { const { exponent, mantissa } = this.getComponents(); return exponent === 2047 && mantissa !== BigInt(0); } /** * Check if the value is infinite */ isInfinite() { const { exponent, mantissa } = this.getComponents(); return exponent === 2047 && mantissa === BigInt(0); } /** * Check if the value is zero */ isZero() { const { exponent, mantissa } = this.getComponents(); return exponent === 0 && mantissa === BigInt(0); } /** * Get string representation */ toString() { return this.toNumber().toString(); } /** * Convert to JSON-safe value: number if within safe range, string if too large * This preserves precision for very large numbers that exceed JavaScript's MAX_SAFE_INTEGER */ toJSONValue() { const num = this.toNumber(); // Check if the number is within JavaScript's safe integer range if (num <= Number.MAX_SAFE_INTEGER && num >= Number.MIN_SAFE_INTEGER) { return num; } // For very large numbers, return as string to preserve precision return num.toString(); } /** * Create F64 from JSON value (handles both number and string inputs) */ static fromJSONValue(value) { if (typeof value === 'string') { return F64.fromString(value); } else { return F64.fromNumber(value); } } /** * Get hex representation of the raw bytes */ toHex() { return this._buffer.toString('hex'); } /** * Create F64 from hex string */ static fromHex(hex) { const buffer = Buffer.from(hex, 'hex'); if (buffer.length !== 8) { throw new Error('Hex string must represent exactly 8 bytes'); } return new F64(buffer); } /** * Compare with another F64 */ equals(other) { return this._buffer.equals(other._buffer); } /** * Equality comparison (alias for equals, for API compatibility) */ eq(other) { if (typeof other === 'number') { return this.toNumber() === other; } return this.equals(other); } /** * Greater than comparison */ gt(other) { if (typeof other === 'number') { return this.toNumber() > other; } return this.toNumber() > other.toNumber(); } /** * Less than comparison */ lt(other) { if (typeof other === 'number') { return this.toNumber() < other; } return this.toNumber() < other.toNumber(); } /** * Greater than or equal comparison */ gte(other) { if (typeof other === 'number') { return this.toNumber() >= other; } return this.toNumber() >= other.toNumber(); } /** * Less than or equal comparison */ lte(other) { if (typeof other === 'number') { return this.toNumber() <= other; } return this.toNumber() <= other.toNumber(); } /** * Create a copy */ clone() { return new F64(this._buffer); } /** * Constants */ static get ZERO() { return F64.fromNumber(0); } static get ONE() { return F64.fromNumber(1); } static get NEGATIVE_ONE() { return F64.fromNumber(-1); } static get INFINITY() { return F64.fromNumber(Number.POSITIVE_INFINITY); } static get NEGATIVE_INFINITY() { return F64.fromNumber(Number.NEGATIVE_INFINITY); } static get NaN() { return F64.fromNumber(Number.NaN); } } exports.F64 = F64; //# sourceMappingURL=F64.js.map