feat: add encodeFloat decodeFloat to typescript

This commit is contained in:
2024-04-15 14:11:54 +02:00
parent 9ccd76c7d9
commit e29cb11b81
6 changed files with 150 additions and 11 deletions

View File

@ -0,0 +1,77 @@
import { test, expect } from "vitest"
import { encodeFloat, decodeFloat } from "./encode"
test("encode_float", () => {
const input = 1.23;
const encoded = encodeFloat(input)
const output = decodeFloat(encoded[0], encoded[1])
console.log(input, output)
expect(output).toBeCloseTo(input);
});
test("encode 2.0", () => {
const input = 2.0;
const encoded = encodeFloat(input)
expect(encoded).toEqual([0, 128])
});
test("floating point imprecision", () => {
let maxError = 0;
new Array(10_000).fill(null).forEach((_, i) => {
const input = i < 5_000 ? i : Math.random() * 100;
const encoded = encodeFloat(input);
const output = decodeFloat(encoded[0], encoded[1]);
const error = Math.abs(input - output);
if (error > maxError) {
maxError = error;
}
});
expect(maxError).toBeLessThan(0.00001);
});
// Test with negative numbers
test("negative numbers", () => {
const inputs = [-1, -0.5, -123.456, -0.0001];
inputs.forEach(input => {
const encoded = encodeFloat(input);
const output = decodeFloat(encoded[0], encoded[1]);
expect(output).toBeCloseTo(input);
});
});
// Test with very small numbers
test("very small numbers", () => {
const input = 1.2345e-38;
const encoded = encodeFloat(input)
const output = decodeFloat(encoded[0], encoded[1])
expect(output).toBeCloseTo(input);
});
// Test with zero
test("zero", () => {
const input = 0;
const encoded = encodeFloat(input)
const output = decodeFloat(encoded[0], encoded[1])
expect(output).toBe(0);
});
// Test with infinity
test("infinity", () => {
const input = Infinity;
const encoded = encodeFloat(input)
const output = decodeFloat(encoded[0], encoded[1])
expect(output).toBe(Infinity);
});
// Test with large numbers
test("large numbers", () => {
const inputs = [1e+5, 1e+10];
inputs.forEach(input => {
const encoded = encodeFloat(input);
const output = decodeFloat(encoded[0], encoded[1]);
// Note: Large numbers may lose precision, hence using toBeCloseTo with a tolerance
expect(output).toBeCloseTo(input, 0);
});
});

View File

@ -0,0 +1,34 @@
export function encodeFloat(f: number): [number, number] {
let buffer = new ArrayBuffer(4); // Create a buffer of 4 bytes (32 bits)
let floatView = new Float32Array(buffer);
let intView = new Uint32Array(buffer);
floatView[0] = f; // Store the float into the buffer
let bits = intView[0]; // Read the bits as integer
let mantissa = bits & 0x007FFFFF;
let exponent = (bits >> 23) & 0xFF;
let sign = (f < 0.0) ? 1 : 0;
// Include the sign bit in the mantissa
mantissa = mantissa | (sign << 23);
return [mantissa, exponent];
}
export function decodeFloat(mantissa: number, exponent: number): number {
let signBit = (mantissa >> 23) & 1;
let mantissaBits = mantissa & 0x007FFFFF;
let exponentBits = (exponent & 0xFF) << 23;
// Reconstruct all bits including sign
let bits = (signBit << 31) | exponentBits | mantissaBits;
let buffer = new ArrayBuffer(4);
let floatView = new Float32Array(buffer);
let intView = new Uint32Array(buffer);
intView[0] = bits; // Set the bits as integer
return floatView[0]; // Read the float back from the buffer
}