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
7 changes: 7 additions & 0 deletions javascript/packages/core/lib/types/float16.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ export function toFloat16Bits(value: number) {
return sign | 0x7c00;
}

if (exponent < -24) {
// Too small for a float16 subnormal. Larger shifts below would wrap
// (JS masks shift counts with & 31) and leave garbage bits, so
// underflow to signed zero.
return sign;
}

if (exponent < -14) {
return sign | ((significand | 0x800000) >> (13 - 14 - exponent));
}
Expand Down
14 changes: 14 additions & 0 deletions javascript/test/number.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,20 @@ describe("number", () => {
expect(result.a).toBe(NaN);
});

test("should float16 underflow tiny magnitudes to signed zero", () => {
// Magnitudes below the smallest float16 subnormal must encode as zero;
// shift counts of 32 or more wrapped (JS masks them with & 31) and left
// garbage bits in the half.
const fory = new Fory({ compatible: false, ref: true });
const { serialize, deserialize } = fory.register(
Type.struct({ typeName: "example.f16zero" }, { a: Type.float16() }),
);
expect(deserialize(serialize({ a: 1e-10 })).a).toBe(0);
expect(deserialize(serialize({ a: 1e-11 })).a).toBe(0);
expect(deserialize(serialize({ a: 1e-40 })).a).toBe(0);
expect(deserialize(serialize({ a: -1e-10 })).a).toBe(-0);
});

test("should float16 Infinity work", () => {
const fory = new Fory({ compatible: false, ref: true });
const serializer = fory.register(
Expand Down
Loading