@node-dlc/messaging
Version:
DLC Messaging Protocol
294 lines • 8.58 kB
JavaScript
"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