Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions javascript/packages/core/lib/typeResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,10 +358,12 @@ export default class TypeResolver {
}
return this.varint32Serializer;
}
if (v > MaxInt32 || v < MinInt32) {
return this.float64Serializer;
// A non-integer number is a float64 value; narrow to float32 only when
// that representation is exact, otherwise precision is silently lost.
if (Math.fround(v) === v) {
return this.float32Serializer;
}
return this.float32Serializer;
return this.float64Serializer;
}

if (typeof v === "bigint") {
Expand Down
33 changes: 32 additions & 1 deletion javascript/test/any.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/

import Fory, { Type } from "../packages/core/index";
import { TypeId } from "../packages/core/lib/type";
import { describe, expect, test } from "@jest/globals";

describe("bool", () => {
Expand Down Expand Up @@ -59,7 +60,7 @@ describe("bool", () => {
test("should write float work", () => {
const fory = new Fory({ compatible: false });
const bin = fory.serialize(123.123);
expect(fory.deserialize(bin).toFixed(3)).toBe("123.123");
expect(fory.deserialize(bin)).toBe(123.123);
});

test("should write bigint work", () => {
Expand Down Expand Up @@ -115,4 +116,34 @@ describe("bool", () => {
const result = deserialize(bin);
expect(result).toEqual("hello");
});

test.each([
[1.5, TypeId.FLOAT32],
[-1.5, TypeId.FLOAT32],
[2 ** -149, TypeId.FLOAT32],
[-(2 ** -149), TypeId.FLOAT32],
[0.1, TypeId.FLOAT64],
[1 / 3, TypeId.FLOAT64],
[-0.7, TypeId.FLOAT64],
[1.5 + Number.EPSILON, TypeId.FLOAT64],
[Number.MIN_VALUE, TypeId.FLOAT64],
[-Number.MIN_VALUE, TypeId.FLOAT64],
[3000000000.5, TypeId.FLOAT64],
])("should dispatch %p as type %p", (value, typeId) => {
const fory = new Fory({ compatible: false });
// Round trips alone also pass if every value is written as float64.
expect(fory.typeResolver.getSerializerByData(value)).toBe(
fory.typeResolver.getSerializerById(typeId),
);
expect(fory.deserialize(fory.serialize(value))).toBe(value);
});

test("should preserve mixed float precision", () => {
// Non-integer numbers narrow to float32 only when exactly representable;
// otherwise the dynamic dispatch must pick float64.
const fory = new Fory({ compatible: false });
const { serialize, deserialize } = fory.register(Type.list(Type.any()));
const values = [0.1, 1 / 3, 1234.5678, -0.7, 1.5, 3000000000.5];
expect(deserialize(serialize(values))).toEqual(values);
});
});
Loading