This is an automated email from the ASF dual-hosted git repository.
chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git
The following commit(s) were added to refs/heads/main by this push:
new 1069909e9 feat(go): add float16 support to go (#3235)
1069909e9 is described below
commit 1069909e9c46ce6545b87c94d232001bab0c05ba
Author: Ayush Kumar <[email protected]>
AuthorDate: Fri Jan 30 17:15:43 2026 +0530
feat(go): add float16 support to go (#3235)
Referenced Issue - #3206
I've gone through the references in the #3206 issue and here are my
implementation plans for the `float16` type in go.
## Implementation Plan
1. Keep all the work in a separate folder under `go/fory/float16`
2. Core Type Definition (`float16.go`): Create a new `Float16` type
based on `uint16` to hold the raw bits, implementing essential constants
(`NaN`, `Inf`) and conversion methods (`FromFloat32()`, `Float32()`)
that handle IEEE 754 logic.
3. Math & Utility Methods: Implement arithmetic operations (`Add`,
`Sub`, `Mul`, `Div`) by temporarily promoting values to `float32` for
calculation, along with classification helpers like `IsNaN`, `IsFinite`,
and strict IEEE 754 comparison methods (`Equal`, `Less`).
4. Serialization Logic (`float16_serializer.go`): Develop a dedicated
`float16Serializer` that handles writing the 2-byte structure to the
wire with the correct `FLOAT16` tag and reading it back.
5. System Integration (`types.go`, `primitive.go`): Update the Fory type
system to recognize `float16` by adding new Dispatch IDs
(`PrimitiveFloat16DispatchId`), registering the serializer, and setting
the fixed size to 2 bytes.
6. I/O & Array Support (`writer.go`, `reader.go`, `array_primitive.go`):
Modify the main read/write loops to process the new Dispatch IDs and add
support for efficient `[]float16` arrays using the `FLOAT16_ARRAY` wire
type.
## What's implemented so far:
1. Added the `Float32FromFloat16()` and `Float32()` which properly
handles the exception types, IsNaN, IsInf, overflow, underflow of
exponential and mantissa.
2. Add other basic functions and comparison methofds to the
`float16.go`.
3. Created `float16_serializer.go` which includes, read and write
methods for `float16`, basically a I/O driver for float16 type.
4. Created `float16_slice_serializer.go`, a serializer for
`[]float16.Float` type.
5. Created `float16_array_serializer.go` a seriailzer for
`[N]float16.Float` type.
---------
Co-authored-by: Shawn Yang <[email protected]>
---
compiler/fory_compiler/generators/go.py | 4 +-
go/fory/array_primitive.go | 77 +++++++
go/fory/array_primitive_test.go | 20 ++
go/fory/float16/float16.go | 363 ++++++++++++++++++++++++++++++++
go/fory/float16/float16_test.go | 102 +++++++++
go/fory/primitive.go | 58 +++++
go/fory/primitive_test.go | 58 +++++
go/fory/reader.go | 2 +
go/fory/slice_primitive.go | 87 ++++++++
go/fory/slice_primitive_test.go | 67 ++++++
go/fory/struct.go | 21 ++
go/fory/struct_test.go | 34 +++
go/fory/type_resolver.go | 13 +-
go/fory/types.go | 42 +++-
go/fory/writer.go | 3 +
15 files changed, 943 insertions(+), 8 deletions(-)
diff --git a/compiler/fory_compiler/generators/go.py
b/compiler/fory_compiler/generators/go.py
index 483cb5679..34ee3410a 100644
--- a/compiler/fory_compiler/generators/go.py
+++ b/compiler/fory_compiler/generators/go.py
@@ -189,7 +189,7 @@ class GoGenerator(BaseGenerator):
PrimitiveKind.UINT64: "uint64",
PrimitiveKind.VAR_UINT64: "uint64",
PrimitiveKind.TAGGED_UINT64: "uint64",
- PrimitiveKind.FLOAT16: "float32",
+ PrimitiveKind.FLOAT16: "float16.Float16",
PrimitiveKind.FLOAT32: "float32",
PrimitiveKind.FLOAT64: "float64",
PrimitiveKind.STRING: "string",
@@ -1077,6 +1077,8 @@ class GoGenerator(BaseGenerator):
if isinstance(field_type, PrimitiveType):
if field_type.kind in (PrimitiveKind.DATE,
PrimitiveKind.TIMESTAMP):
imports.add('"time"')
+ elif field_type.kind == PrimitiveKind.FLOAT16:
+ imports.add('float16 "github.com/apache/fory/go/fory/float16"')
elif isinstance(field_type, ListType):
self.collect_imports(field_type.element_type, imports)
diff --git a/go/fory/array_primitive.go b/go/fory/array_primitive.go
index a50367287..977773359 100644
--- a/go/fory/array_primitive.go
+++ b/go/fory/array_primitive.go
@@ -20,6 +20,8 @@ package fory
import (
"reflect"
"unsafe"
+
+ "github.com/apache/fory/go/fory/float16"
)
// ============================================================================
@@ -794,3 +796,78 @@ func (s uint64ArraySerializer) Read(ctx *ReadContext,
refMode RefMode, readType
func (s uint64ArraySerializer) ReadWithTypeInfo(ctx *ReadContext, refMode
RefMode, typeInfo *TypeInfo, value reflect.Value) {
s.Read(ctx, refMode, false, false, value)
}
+
+// ============================================================================
+// float16ArraySerializer - optimized [N]float16.Float16 serialization
+// ============================================================================
+
+type float16ArraySerializer struct {
+ arrayType reflect.Type
+}
+
+func (s float16ArraySerializer) WriteData(ctx *WriteContext, value
reflect.Value) {
+ buf := ctx.Buffer()
+ length := value.Len()
+ size := length * 2
+ buf.WriteLength(size)
+ if length > 0 {
+ if value.CanAddr() && isLittleEndian {
+ ptr := value.Addr().UnsafePointer()
+ buf.WriteBinary(unsafe.Slice((*byte)(ptr), size))
+ } else {
+ for i := 0; i < length; i++ {
+ // We can't easily cast the whole array if not
addressable/little-endian
+ // So we iterate.
+ // value.Index(i) is Float16, we cast to uint16
+ val :=
value.Index(i).Interface().(float16.Float16)
+ buf.WriteUint16(val.Bits())
+ }
+ }
+ }
+}
+
+func (s float16ArraySerializer) Write(ctx *WriteContext, refMode RefMode,
writeType bool, hasGenerics bool, value reflect.Value) {
+ writeArrayRefAndType(ctx, refMode, writeType, value, FLOAT16_ARRAY)
+ if ctx.HasError() {
+ return
+ }
+ s.WriteData(ctx, value)
+}
+
+func (s float16ArraySerializer) ReadData(ctx *ReadContext, value
reflect.Value) {
+ buf := ctx.Buffer()
+ ctxErr := ctx.Err()
+ size := buf.ReadLength(ctxErr)
+ length := size / 2
+ if ctx.HasError() {
+ return
+ }
+ if length != value.Type().Len() {
+ ctx.SetError(DeserializationErrorf("array length %d does not
match type %v", length, value.Type()))
+ return
+ }
+
+ if length > 0 {
+ if isLittleEndian {
+ ptr := value.Addr().UnsafePointer()
+ raw := buf.ReadBinary(size, ctxErr)
+ copy(unsafe.Slice((*byte)(ptr), size), raw)
+ } else {
+ for i := 0; i < length; i++ {
+
value.Index(i).Set(reflect.ValueOf(float16.Float16FromBits(buf.ReadUint16(ctxErr))))
+ }
+ }
+ }
+}
+
+func (s float16ArraySerializer) Read(ctx *ReadContext, refMode RefMode,
readType bool, hasGenerics bool, value reflect.Value) {
+ done := readArrayRefAndType(ctx, refMode, readType, value)
+ if done || ctx.HasError() {
+ return
+ }
+ s.ReadData(ctx, value)
+}
+
+func (s float16ArraySerializer) ReadWithTypeInfo(ctx *ReadContext, refMode
RefMode, typeInfo *TypeInfo, value reflect.Value) {
+ s.Read(ctx, refMode, false, false, value)
+}
diff --git a/go/fory/array_primitive_test.go b/go/fory/array_primitive_test.go
index a55b6ede8..c2e684af4 100644
--- a/go/fory/array_primitive_test.go
+++ b/go/fory/array_primitive_test.go
@@ -20,6 +20,7 @@ package fory
import (
"testing"
+ "github.com/apache/fory/go/fory/float16"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -130,3 +131,22 @@ func TestArraySliceInteroperability(t *testing.T) {
assert.Contains(t, err.Error(), "array length")
})
}
+
+func TestFloat16Array(t *testing.T) {
+ f := NewFory()
+
+ t.Run("float16_array", func(t *testing.T) {
+ arr := [3]float16.Float16{
+ float16.Float16FromFloat32(1.0),
+ float16.Float16FromFloat32(2.5),
+ float16.Float16FromFloat32(-0.5),
+ }
+ data, err := f.Serialize(arr)
+ assert.NoError(t, err)
+
+ var result [3]float16.Float16
+ err = f.Deserialize(data, &result)
+ assert.NoError(t, err)
+ assert.Equal(t, arr, result)
+ })
+}
diff --git a/go/fory/float16/float16.go b/go/fory/float16/float16.go
new file mode 100644
index 000000000..1a7ca38d5
--- /dev/null
+++ b/go/fory/float16/float16.go
@@ -0,0 +1,363 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package float16
+
+import (
+ "fmt"
+ "math"
+)
+
+// Float16 represents a half-precision floating point number (IEEE 754-2008
binary16).
+// It is stored as a uint16.
+type Float16 uint16
+
+// Constants for half-precision floating point
+const (
+ uvNan = 0x7e00 // 0 11111 1000000000 (standard quiet NaN)
+ uvInf = 0x7c00 // 0 11111 0000000000 (+Inf)
+ uvNegInf = 0xfc00 // 1 11111 0000000000 (-Inf)
+ uvNegZero = 0x8000 // 1 00000 0000000000 (-0)
+ uvMax = 0x7bff // 65504
+ uvMinNorm = 0x0400 // 2^-14 (highest subnormal is 0x03ff, lowest normal
is 0x0400)
+ uvMinSub = 0x0001 // 2^-24
+ uvOne = 0x3c00 // 1.0
+ maskSign = 0x8000
+ maskExp = 0x7c00
+ maskMant = 0x03ff
+)
+
+// Common values
+var (
+ NaN = Float16(uvNan)
+ Inf = Float16(uvInf)
+ NegInf = Float16(uvNegInf)
+ Zero = Float16(0)
+ NegZero = Float16(uvNegZero)
+ Max = Float16(uvMax)
+ Smallest = Float16(uvMinSub) // Smallest non-zero
+ One = Float16(uvOne)
+)
+
+// Float16FromBits returns the Float16 corresponding to the given bit pattern.
+func Float16FromBits(b uint16) Float16 {
+ return Float16(b)
+}
+
+// Bits returns the raw bit pattern of the floating point number.
+func (f Float16) Bits() uint16 {
+ return uint16(f)
+}
+
+// Float16FromFloat32 converts a float32 to a Float16.
+// Rounds to nearest, ties to even.
+func Float16FromFloat32(f32 float32) Float16 {
+ bits := math.Float32bits(f32)
+ sign := (bits >> 31) & 0x1
+ exp := (bits >> 23) & 0xff
+ mant := bits & 0x7fffff
+
+ var outSign uint16 = uint16(sign) << 15
+ var outExp uint16
+ var outMant uint16
+
+ if exp == 0xff {
+ // NaN or Inf
+ outExp = 0x1f
+ if mant != 0 {
+ // NaN - preserve top bit of mantissa for
quiet/signaling if possible, but simplest is canonical QNaN
+ outMant = 0x200 | (uint16(mant>>13) & 0x1ff)
+ if outMant == 0 {
+ outMant = 0x200 // Ensure at least one bit
+ }
+ } else {
+ // Inf
+ outMant = 0
+ }
+ } else if exp == 0 {
+ // Signed zero or subnormal float32 (which becomes zero in
float16 usually)
+ outExp = 0
+ outMant = 0
+ } else {
+ // Normalized
+ newExp := int(exp) - 127 + 15
+ if newExp >= 31 {
+ // Overflow to Inf
+ outExp = 0x1f
+ outMant = 0
+ } else if newExp <= 0 {
+ // Underflow to subnormal or zero
+ // Shift mantissa to align with float16 subnormal range
+ // float32 mantissa has implicit 1.
+ fullMant := mant | 0x800000
+ shift := 1 - newExp // 1 for implicit bit alignment
+ // We need to round.
+ // Mantissa bits: 23. Subnormal 16 mant bits: 10.
+ // We want to shift right by (13 + shift).
+
+ // Let's do a more precise soft-float rounding
+ // Re-assemble float value to handle subnormal rounding
correctly is hard with just bit shifts
+ // But since we have hardware float32...
+ // Actually pure bit manipulation is robust if careful.
+
+ // Shift right amount
+ netShift := 13 + shift // 23 - 10 + shift
+
+ if netShift >= 24 {
+ // Too small, becomes zero
+ outExp = 0
+ outMant = 0
+ } else {
+ outExp = 0
+ roundBit := (fullMant >> (netShift - 1)) & 1
+ sticky := (fullMant & ((1 << (netShift - 1)) -
1))
+ outMant = uint16(fullMant >> netShift)
+
+ if roundBit == 1 {
+ if sticky != 0 || (outMant&1) == 1 {
+ outMant++
+ }
+ }
+ }
+ } else {
+ // Normal range
+ outExp = uint16(newExp)
+ // Mantissa: float32 has 23 bits, float16 has 10.
+ // We need to round based on the dropped 13 bits.
+ // Last kept bit at index 13 (0-indexed from LSB of
float32 mant)
+ // Round bit is index 12.
+
+ // Using helper to round
+ outMant = uint16(mant >> 13)
+ roundBit := (mant >> 12) & 1
+ sticky := mant & 0xfff
+
+ if roundBit == 1 {
+ // Round to nearest, ties to even
+ if sticky != 0 || (outMant&1) == 1 {
+ outMant++
+ if outMant > 0x3ff {
+ // Overflow mantissa, increment
exponent
+ outMant = 0
+ outExp++
+ if outExp >= 31 {
+ outExp = 0x1f // Inf
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return Float16(outSign | (outExp << 10) | outMant)
+}
+
+// Float32 returns the float32 representation of the Float16.
+func (f Float16) Float32() float32 {
+ bits := uint16(f)
+ sign := (bits >> 15) & 0x1
+ exp := (bits >> 10) & 0x1f
+ mant := bits & 0x3ff
+
+ var outBits uint32
+ outBits = uint32(sign) << 31
+
+ if exp == 0x1f {
+ // NaN or Inf
+ outBits |= 0xff << 23
+ if mant != 0 {
+ // NaN - promote mantissa
+ outBits |= uint32(mant) << 13
+ }
+ } else if exp == 0 {
+ if mant == 0 {
+ // Signed zero
+ outBits |= 0
+ } else {
+ // Subnormal
+ // Convert to float32 normal
+ // Normalize the subnormal
+ shift := 0
+ m := uint32(mant)
+ for (m & 0x400) == 0 {
+ m <<= 1
+ shift++
+ }
+ // m now has bit 10 set (implicit 1 for float32)
+ // discard implicit bit
+ m &= 0x3ff
+ // new float32 exponent
+ // subnormal 16 is 2^-14 * 0.mant
+ // = 2^-14 * 2^-10 * mant_integer
+ // = 2^-24 * mant_integer
+ // Normalized float32 is 1.mant * 2^(E-127)
+ // We effectively shift left until we hit the 1.
+ // The effective exponent is (1 - 15) - shift = -14 -
shift?
+ // Simpler:
+ // value = mant * 2^-24
+ // Reconstruct using float32 operations to avoid bit
headaches?
+ // No, bit ops are faster.
+
+ // Float16 subnormal: (-1)^S * 2^(1-15) * (mant / 1024)
+ // = (-1)^S * 2^-14 * (mant * 2^-10)
+ // = (-1)^S * 2^-24 * mant
+
+ // Float32: (-1)^S * 2^(E-127) * (1 + M/2^23)
+
+ // Let's use the magic number method or just float32
arithmetic if lazy
+ val := float32(mant) * float32(math.Pow(2, -24))
+ if sign == 1 {
+ val = -val
+ }
+ return float32(val)
+ }
+ } else {
+ // Normal
+ outBits |= (uint32(exp) - 15 + 127) << 23
+ outBits |= uint32(mant) << 13
+ }
+
+ return math.Float32frombits(outBits)
+}
+
+// IsNaN reports whether f is an IEEE 754 “not-a-number” value.
+func (f Float16) IsNaN() bool {
+ return (f&maskExp) == maskExp && (f&maskMant) != 0
+}
+
+// IsInf reports whether f is an infinity, according to sign.
+// If sign > 0, IsInf reports whether f is positive infinity.
+// If sign < 0, IsInf reports whether f is negative infinity.
+// If sign == 0, IsInf reports whether f is either infinity.
+func (f Float16) IsInf(sign int) bool {
+ isInf := (f&maskExp) == maskExp && (f&maskMant) == 0
+ if !isInf {
+ return false
+ }
+ if sign == 0 {
+ return true
+ }
+ hasSign := (f & maskSign) != 0
+ if sign > 0 {
+ return !hasSign
+ }
+ return hasSign
+}
+
+// IsZero reports whether f is +0 or -0.
+func (f Float16) IsZero() bool {
+ return (f & (maskExp | maskMant)) == 0
+}
+
+// IsFinite reports whether f is neither NaN nor an infinity.
+func (f Float16) IsFinite() bool {
+ return (f & maskExp) != maskExp
+}
+
+// IsNormal reports whether f is a normal value (not zero, subnormal,
infinite, or NaN).
+func (f Float16) IsNormal() bool {
+ exp := f & maskExp
+ return exp != 0 && exp != maskExp
+}
+
+// IsSubnormal reports whether f is a subnormal value.
+func (f Float16) IsSubnormal() bool {
+ exp := f & maskExp
+ mant := f & maskMant
+ return exp == 0 && mant != 0
+}
+
+// Signbit reports whether f is negative or negative zero.
+func (f Float16) Signbit() bool {
+ return (f & maskSign) != 0
+}
+
+// String returns the string representation of f.
+func (f Float16) String() string {
+ return fmt.Sprintf("%g", f.Float32())
+}
+
+// Arithmetic operations (promoted to float32)
+
+func (f Float16) Add(other Float16) Float16 {
+ return Float16FromFloat32(f.Float32() + other.Float32())
+}
+
+func (f Float16) Sub(other Float16) Float16 {
+ return Float16FromFloat32(f.Float32() - other.Float32())
+}
+
+func (f Float16) Mul(other Float16) Float16 {
+ return Float16FromFloat32(f.Float32() * other.Float32())
+}
+
+func (f Float16) Div(other Float16) Float16 {
+ return Float16FromFloat32(f.Float32() / other.Float32())
+}
+
+func (f Float16) Neg() Float16 {
+ return f ^ maskSign
+}
+
+func (f Float16) Abs() Float16 {
+ return f &^ maskSign
+}
+
+// Comparison
+
+func (f Float16) Equal(other Float16) bool {
+ // IEEE 754: NaN != NaN
+ if f.IsNaN() || other.IsNaN() {
+ return false
+ }
+ // +0 == -0
+ if f.IsZero() && other.IsZero() {
+ return true
+ }
+ // Direct bit comparison works for typical normals with same sign
+ // But mixed signs or negative numbers need caear
+ return f.Float32() == other.Float32()
+}
+
+func (f Float16) Less(other Float16) bool {
+ if f.IsNaN() || other.IsNaN() {
+ return false
+ }
+ // Handle signed zero: -0 is not less than +0
+ if f.IsZero() && other.IsZero() {
+ return false
+ }
+ return f.Float32() < other.Float32()
+}
+
+func (f Float16) LessEq(other Float16) bool {
+ if f.IsNaN() || other.IsNaN() {
+ return false
+ }
+ if f.IsZero() && other.IsZero() {
+ return true
+ }
+ return f.Float32() <= other.Float32()
+}
+
+func (f Float16) Greater(other Float16) bool {
+ return other.Less(f)
+}
+
+func (f Float16) GreaterEq(other Float16) bool {
+ return other.LessEq(f)
+}
diff --git a/go/fory/float16/float16_test.go b/go/fory/float16/float16_test.go
new file mode 100644
index 000000000..969c04c6e
--- /dev/null
+++ b/go/fory/float16/float16_test.go
@@ -0,0 +1,102 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package float16_test
+
+import (
+ "math"
+ "testing"
+
+ "github.com/apache/fory/go/fory/float16"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestFloat16_Conversion(t *testing.T) {
+ tests := []struct {
+ name string
+ f32 float32
+ want uint16 // bits
+ check bool // if true, check exact bits, else check float32
roundtrip within epsilon
+ }{
+ {"Zero", 0.0, 0x0000, true},
+ {"NegZero", float32(math.Copysign(0, -1)), 0x8000, true},
+ {"One", 1.0, 0x3c00, true},
+ {"MinusOne", -1.0, 0xbc00, true},
+ {"Max", 65504.0, 0x7bff, true},
+ {"Inf", float32(math.Inf(1)), 0x7c00, true},
+ {"NegInf", float32(math.Inf(-1)), 0xfc00, true},
+ // Smallest normal: 2^-14 = 0.000061035156
+ {"SmallestNormal", float32(math.Pow(2, -14)), 0x0400, true},
+ // Largest subnormal: 2^-14 - 2^-24 = 6.09756...e-5
+ {"LargestSubnormal", float32(6.097555e-5), 0x03ff, true},
+ // Smallest subnormal: 2^-24
+ {"SmallestSubnormal", float32(math.Pow(2, -24)), 0x0001, true},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ f16 := float16.Float16FromFloat32(tt.f32)
+ if tt.check {
+ assert.Equal(t, tt.want, f16.Bits(), "Bits
match")
+ }
+
+ // Round trip check
+ roundTrip := f16.Float32()
+ if math.IsInf(float64(tt.f32), 0) {
+ assert.True(t, math.IsInf(float64(roundTrip),
0))
+ assert.Equal(t, math.Signbit(float64(tt.f32)),
math.Signbit(float64(roundTrip)))
+ } else if math.IsNaN(float64(tt.f32)) {
+ assert.True(t, math.IsNaN(float64(roundTrip)))
+ } else {
+ // Allow small error due to precision loss
+ // Epsilon for float16 is 2^-10 ~= 0.001
relative error
+ // But we check consistency
+ if tt.check {
+ // bit exact means round trip should
map back to similar float (precision loss expected)
+ // Verify that converting back to f16
gives same bits
+ f16back :=
float16.Float16FromFloat32(roundTrip)
+ assert.Equal(t, tt.want, f16back.Bits())
+ }
+ }
+ })
+ }
+}
+
+func TestFloat16_NaN(t *testing.T) {
+ nan := float16.NaN
+ assert.True(t, nan.IsNaN())
+ assert.False(t, nan.IsInf(0))
+ assert.False(t, nan.IsZero())
+
+ // Comparison
+ assert.False(t, nan.Equal(nan))
+
+ // Conversion
+ f32 := nan.Float32()
+ assert.True(t, math.IsNaN(float64(f32)))
+}
+
+func TestFloat16_Arithmetic(t *testing.T) {
+ one := float16.Float16FromFloat32(1.0)
+ two := float16.Float16FromFloat32(2.0)
+ three := float16.Float16FromFloat32(3.0)
+
+ assert.Equal(t, "3", one.Add(two).String())
+ assert.Equal(t, "2", three.Sub(one).String())
+ assert.Equal(t, "6", two.Mul(three).String())
+ assert.Equal(t, "1.5", three.Div(two).String())
+}
diff --git a/go/fory/primitive.go b/go/fory/primitive.go
index 63f0c53ac..3057d0dfa 100644
--- a/go/fory/primitive.go
+++ b/go/fory/primitive.go
@@ -605,3 +605,61 @@ func (s float64Serializer) Read(ctx *ReadContext, refMode
RefMode, readType bool
func (s float64Serializer) ReadWithTypeInfo(ctx *ReadContext, refMode RefMode,
typeInfo *TypeInfo, value reflect.Value) {
s.Read(ctx, refMode, false, false, value)
}
+
+// ============================================================================
+// float16Serializer - optimized float16 serialization
+// ============================================================================
+
+// float16Serializer handles float16 type
+type float16Serializer struct{}
+
+var globalFloat16Serializer = float16Serializer{}
+
+func (s float16Serializer) WriteData(ctx *WriteContext, value reflect.Value) {
+ // Value is effectively uint16 (alias)
+ // We can use WriteUint16, but we check if it is indeed float16
compatible
+ // The value comes from reflection, likely an interface or concrete type
+ // Since Float16 is uint16, value.Uint() works.
+ ctx.buffer.WriteUint16(uint16(value.Uint()))
+}
+
+func (s float16Serializer) Write(ctx *WriteContext, refMode RefMode, writeType
bool, hasGenerics bool, value reflect.Value) {
+ if refMode != RefModeNone {
+ ctx.buffer.WriteInt8(NotNullValueFlag)
+ }
+ if writeType {
+ ctx.buffer.WriteVarUint32Small7(uint32(FLOAT16))
+ }
+ s.WriteData(ctx, value)
+}
+
+func (s float16Serializer) ReadData(ctx *ReadContext, value reflect.Value) {
+ err := ctx.Err()
+ // Read uint16 bits
+ bits := ctx.buffer.ReadUint16(err)
+ if ctx.HasError() {
+ return
+ }
+ // Set the value. Since Float16 is uint16, SetUint works.
+ value.SetUint(uint64(bits))
+}
+
+func (s float16Serializer) Read(ctx *ReadContext, refMode RefMode, readType
bool, hasGenerics bool, value reflect.Value) {
+ err := ctx.Err()
+ if refMode != RefModeNone {
+ if ctx.buffer.ReadInt8(err) == NullFlag {
+ return
+ }
+ }
+ if readType {
+ _ = ctx.buffer.ReadVarUint32Small7(err)
+ }
+ if ctx.HasError() {
+ return
+ }
+ s.ReadData(ctx, value)
+}
+
+func (s float16Serializer) ReadWithTypeInfo(ctx *ReadContext, refMode RefMode,
typeInfo *TypeInfo, value reflect.Value) {
+ s.Read(ctx, refMode, false, false, value)
+}
diff --git a/go/fory/primitive_test.go b/go/fory/primitive_test.go
new file mode 100644
index 000000000..978a81d46
--- /dev/null
+++ b/go/fory/primitive_test.go
@@ -0,0 +1,58 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package fory
+
+import (
+ "testing"
+
+ "github.com/apache/fory/go/fory/float16"
+ "github.com/stretchr/testify/require"
+)
+
+func TestFloat16Primitive(t *testing.T) {
+ f := New(WithXlang(true))
+ f16 := float16.Float16FromFloat32(3.14)
+
+ // Directly serialize a float16 value
+ data, err := f.Serialize(f16)
+ require.NoError(t, err)
+
+ var res float16.Float16
+ err = f.Deserialize(data, &res)
+ require.NoError(t, err)
+
+ require.True(t, f16.Equal(res))
+
+ // Value check (approximate)
+ require.InDelta(t, 3.14, res.Float32(), 0.01)
+}
+
+func TestFloat16PrimitiveSliceDirect(t *testing.T) {
+ // Tests serializing a slice as a root object
+ f := New(WithXlang(true))
+ f16 := float16.Float16FromFloat32(3.14)
+
+ slice := []float16.Float16{f16, float16.Zero}
+ data, err := f.Serialize(slice)
+ require.NoError(t, err)
+
+ var resSlice []float16.Float16
+ err = f.Deserialize(data, &resSlice)
+ require.NoError(t, err)
+ require.Equal(t, slice, resSlice)
+}
diff --git a/go/fory/reader.go b/go/fory/reader.go
index bcbd1048c..dd9a837bb 100644
--- a/go/fory/reader.go
+++ b/go/fory/reader.go
@@ -222,6 +222,8 @@ func (c *ReadContext) readFast(ptr unsafe.Pointer, ct
DispatchId) {
*(*float32)(ptr) = c.buffer.ReadFloat32(err)
case PrimitiveFloat64DispatchId:
*(*float64)(ptr) = c.buffer.ReadFloat64(err)
+ case PrimitiveFloat16DispatchId:
+ *(*uint16)(ptr) = c.buffer.ReadUint16(err)
case StringDispatchId:
*(*string)(ptr) = readString(c.buffer, err)
}
diff --git a/go/fory/slice_primitive.go b/go/fory/slice_primitive.go
index 36818de3e..cf83ab76b 100644
--- a/go/fory/slice_primitive.go
+++ b/go/fory/slice_primitive.go
@@ -21,6 +21,8 @@ import (
"reflect"
"strconv"
"unsafe"
+
+ "github.com/apache/fory/go/fory/float16"
)
// isNilSlice checks if a value is a nil slice. Safe to call on any value type.
@@ -828,6 +830,91 @@ func ReadFloat64Slice(buf *ByteBuffer, err *Error)
[]float64 {
return result
}
+// ============================================================================
+// float16SliceSerializer - optimized []float16.Float16 serialization
+// ============================================================================
+
+type float16SliceSerializer struct{}
+
+func (s float16SliceSerializer) WriteData(ctx *WriteContext, value
reflect.Value) {
+ // Cast to []float16.Float16
+ v := value.Interface().([]float16.Float16)
+ buf := ctx.Buffer()
+ length := len(v)
+ size := length * 2
+ buf.WriteLength(size)
+ if length > 0 {
+ // Float16 is uint16 underneath, so we can cast slice pointer
+ ptr := unsafe.Pointer(&v[0])
+ if isLittleEndian {
+ buf.WriteBinary(unsafe.Slice((*byte)(ptr), size))
+ } else {
+ // Big-endian architectures need explicit byte swapping
+ for i := 0; i < length; i++ {
+ // We can just write as uint16, WriteUint16
handles endianness for us
+ // Float16.Bits() returns uint16
+ buf.WriteUint16(v[i].Bits())
+ }
+ }
+ }
+}
+
+func (s float16SliceSerializer) Write(ctx *WriteContext, refMode RefMode,
writeType bool, hasGenerics bool, value reflect.Value) {
+ done := writeSliceRefAndType(ctx, refMode, writeType, value,
FLOAT16_ARRAY)
+ if done || ctx.HasError() {
+ return
+ }
+ s.WriteData(ctx, value)
+}
+
+func (s float16SliceSerializer) Read(ctx *ReadContext, refMode RefMode,
readType bool, hasGenerics bool, value reflect.Value) {
+ done, typeId := readSliceRefAndType(ctx, refMode, readType, value)
+ if done || ctx.HasError() {
+ return
+ }
+ if readType && typeId != uint32(FLOAT16_ARRAY) {
+ ctx.SetError(DeserializationErrorf("slice type mismatch:
expected FLOAT16_ARRAY (%d), got %d", FLOAT16_ARRAY, typeId))
+ return
+ }
+ s.ReadData(ctx, value)
+}
+
+func (s float16SliceSerializer) ReadWithTypeInfo(ctx *ReadContext, refMode
RefMode, typeInfo *TypeInfo, value reflect.Value) {
+ s.Read(ctx, refMode, false, false, value)
+}
+
+func (s float16SliceSerializer) ReadData(ctx *ReadContext, value
reflect.Value) {
+ buf := ctx.Buffer()
+ ctxErr := ctx.Err()
+ size := buf.ReadLength(ctxErr)
+ length := size / 2
+ if ctx.HasError() {
+ return
+ }
+
+ // Ensure capacity
+ ptr := (*[]float16.Float16)(value.Addr().UnsafePointer())
+ if length == 0 {
+ *ptr = make([]float16.Float16, 0)
+ return
+ }
+
+ result := make([]float16.Float16, length)
+
+ if isLittleEndian {
+ raw := buf.ReadBinary(size, ctxErr)
+ // unsafe copy
+ targetPtr := unsafe.Pointer(&result[0])
+ copy(unsafe.Slice((*byte)(targetPtr), size), raw)
+ } else {
+ for i := 0; i < length; i++ {
+ // ReadUint16 handles endianness
+ result[i] =
float16.Float16FromBits(buf.ReadUint16(ctxErr))
+ }
+ }
+ *ptr = result
+}
+
// WriteIntSlice writes []int to buffer using ARRAY protocol
//
//go:inline
diff --git a/go/fory/slice_primitive_test.go b/go/fory/slice_primitive_test.go
new file mode 100644
index 000000000..61ff7b89c
--- /dev/null
+++ b/go/fory/slice_primitive_test.go
@@ -0,0 +1,67 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package fory
+
+import (
+ "testing"
+
+ "github.com/apache/fory/go/fory/float16"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestFloat16Slice(t *testing.T) {
+ f := NewFory()
+
+ t.Run("float16_slice", func(t *testing.T) {
+ slice := []float16.Float16{
+ float16.Float16FromFloat32(1.0),
+ float16.Float16FromFloat32(2.5),
+ float16.Float16FromFloat32(-0.5),
+ }
+ data, err := f.Serialize(slice)
+ assert.NoError(t, err)
+
+ var result []float16.Float16
+ err = f.Deserialize(data, &result)
+ assert.NoError(t, err)
+ assert.Equal(t, slice, result)
+ })
+
+ t.Run("float16_slice_empty", func(t *testing.T) {
+ slice := []float16.Float16{}
+ data, err := f.Serialize(slice)
+ assert.NoError(t, err)
+
+ var result []float16.Float16
+ err = f.Deserialize(data, &result)
+ assert.NoError(t, err)
+ assert.NotNil(t, result)
+ assert.Empty(t, result)
+ })
+
+ t.Run("float16_slice_nil", func(t *testing.T) {
+ var slice []float16.Float16 = nil
+ data, err := f.Serialize(slice)
+ assert.NoError(t, err)
+
+ var result []float16.Float16
+ err = f.Deserialize(data, &result)
+ assert.NoError(t, err)
+ assert.Nil(t, result)
+ })
+}
diff --git a/go/fory/struct.go b/go/fory/struct.go
index 828dd28cd..ff4aaece9 100644
--- a/go/fory/struct.go
+++ b/go/fory/struct.go
@@ -1389,6 +1389,17 @@ func (s *structSerializer) WriteData(ctx *WriteContext,
value reflect.Value) {
} else {
binary.LittleEndian.PutUint64(data[bufOffset:], math.Float64bits(v))
}
+ case PrimitiveFloat16DispatchId:
+ v, ok := loadFieldValue[uint16](field.Kind,
fieldPtr, optInfo)
+ if !ok {
+ v = 0
+ }
+ if isLittleEndian {
+
*(*uint16)(unsafe.Pointer(&data[bufOffset])) = v
+ } else {
+
binary.LittleEndian.PutUint16(data[bufOffset:], v)
+ }
+
}
}
// Update writer index ONCE after all fixed fields
@@ -2562,6 +2573,16 @@ func (s *structSerializer) ReadData(ctx *ReadContext,
value reflect.Value) {
v =
math.Float64frombits(binary.LittleEndian.Uint64(data[bufOffset:]))
}
storeFieldValue(field.Kind, fieldPtr, optInfo,
v)
+ case PrimitiveFloat16DispatchId:
+ var v uint16
+ if isLittleEndian {
+ v =
*(*uint16)(unsafe.Pointer(&data[bufOffset]))
+ } else {
+ v =
binary.LittleEndian.Uint16(data[bufOffset:])
+ }
+ // Float16 is underlying uint16, so we can
store it directly
+ storeFieldValue(field.Kind, fieldPtr, optInfo,
v)
+
}
}
// Update reader index ONCE after all fixed fields
diff --git a/go/fory/struct_test.go b/go/fory/struct_test.go
index 51fb806f3..7f345b724 100644
--- a/go/fory/struct_test.go
+++ b/go/fory/struct_test.go
@@ -21,6 +21,7 @@ import (
"reflect"
"testing"
+ "github.com/apache/fory/go/fory/float16"
"github.com/apache/fory/go/fory/optional"
"github.com/stretchr/testify/require"
)
@@ -474,3 +475,36 @@ func TestSkipAnyValueReadsSharedTypeMeta(t *testing.T) {
require.True(t, ok)
require.Equal(t, "ok", result.Name)
}
+
+func TestFloat16StructField(t *testing.T) {
+ type StructWithFloat16 struct {
+ F16 float16.Float16
+ SliceF16 []float16.Float16
+ ArrayF16 [3]float16.Float16
+ }
+
+ f := New(WithXlang(true))
+ require.NoError(t, f.RegisterStruct(StructWithFloat16{}, 3001))
+
+ val := &StructWithFloat16{
+ F16: float16.Float16FromFloat32(1.5),
+ SliceF16: []float16.Float16{float16.Float16FromFloat32(1.0),
float16.Float16FromFloat32(2.5)},
+ ArrayF16: [3]float16.Float16{float16.Zero, float16.One,
float16.NegZero},
+ }
+
+ data, err := f.Serialize(val)
+ require.NoError(t, err)
+
+ // Create new instance
+ res := &StructWithFloat16{}
+ err = f.Deserialize(data, res)
+ require.NoError(t, err)
+
+ // Verify
+ require.Equal(t, val.F16, res.F16)
+ require.Equal(t, val.SliceF16, res.SliceF16)
+ require.Equal(t, val.ArrayF16, res.ArrayF16)
+
+ // Specific value check
+ require.Equal(t, float32(1.5), res.F16.Float32())
+}
diff --git a/go/fory/type_resolver.go b/go/fory/type_resolver.go
index e3ef79823..0069dfc0d 100644
--- a/go/fory/type_resolver.go
+++ b/go/fory/type_resolver.go
@@ -29,6 +29,7 @@ import (
"time"
"unsafe"
+ "github.com/apache/fory/go/fory/float16"
"github.com/apache/fory/go/fory/meta"
)
@@ -68,6 +69,7 @@ var (
uintSliceType = reflect.TypeOf((*[]uint)(nil)).Elem()
float32SliceType = reflect.TypeOf((*[]float32)(nil)).Elem()
float64SliceType = reflect.TypeOf((*[]float64)(nil)).Elem()
+ float16SliceType = reflect.TypeOf((*[]float16.Float16)(nil)).Elem()
interfaceSliceType = reflect.TypeOf((*[]any)(nil)).Elem()
interfaceMapType = reflect.TypeOf((*map[any]any)(nil)).Elem()
stringStringMapType = reflect.TypeOf((*map[string]string)(nil)).Elem()
@@ -93,6 +95,7 @@ var (
intType = reflect.TypeOf((*int)(nil)).Elem()
float32Type = reflect.TypeOf((*float32)(nil)).Elem()
float64Type = reflect.TypeOf((*float64)(nil)).Elem()
+ float16Type = reflect.TypeOf((*float16.Float16)(nil)).Elem()
dateType = reflect.TypeOf((*Date)(nil)).Elem()
timestampType = reflect.TypeOf((*time.Time)(nil)).Elem()
genericSetType = reflect.TypeOf((*Set[any])(nil)).Elem()
@@ -243,6 +246,7 @@ func newTypeResolver(fory *Fory) *TypeResolver {
int64Type,
float32Type,
float64Type,
+ float16Type,
stringType,
dateType,
timestampType,
@@ -396,6 +400,7 @@ func (r *TypeResolver) initialize() {
{uintSliceType, INT64_ARRAY, uintSliceSerializer{}},
{float32SliceType, FLOAT32_ARRAY, float32SliceSerializer{}},
{float64SliceType, FLOAT64_ARRAY, float64SliceSerializer{}},
+ {float16SliceType, FLOAT16_ARRAY, float16SliceSerializer{}},
// Register common map types for fast path with optimized
serializers
{stringStringMapType, MAP, stringStringMapSerializer{}},
{stringInt64MapType, MAP, stringInt64MapSerializer{}},
@@ -419,6 +424,7 @@ func (r *TypeResolver) initialize() {
{intType, VARINT64, intSerializer{}}, // int maps to int64 for
xlang
{float32Type, FLOAT32, float32Serializer{}},
{float64Type, FLOAT64, float64Serializer{}},
+ {float16Type, FLOAT16, float16Serializer{}},
{dateType, DATE, dateSerializer{}},
{timestampType, TIMESTAMP, timeSerializer{}},
{genericSetType, SET, setSerializer{}},
@@ -426,7 +432,7 @@ func (r *TypeResolver) initialize() {
for _, elem := range serializers {
_, err := r.registerType(elem.Type, uint32(elem.TypeId), "",
"", elem.Serializer, true)
if err != nil {
- fmt.Errorf("init type error: %v", err)
+ panic(fmt.Errorf("init type error: %v", err))
}
}
@@ -1610,6 +1616,11 @@ func (r *TypeResolver) createSerializer(type_
reflect.Type, mapInStruct bool) (s
}
return int32ArraySerializer{arrayType: type_}, nil
case reflect.Uint16:
+ // Check for fory.Float16 (aliased to uint16)
+ // Check name first to avoid slow PkgPath call
+ if elem.Name() == "Float16" && (elem.PkgPath() ==
"github.com/apache/fory/go/fory/float16" || strings.HasSuffix(elem.PkgPath(),
"/float16")) {
+ return float16ArraySerializer{arrayType:
type_}, nil
+ }
return uint16ArraySerializer{arrayType: type_}, nil
case reflect.Uint32:
return uint32ArraySerializer{arrayType: type_}, nil
diff --git a/go/fory/types.go b/go/fory/types.go
index df9112451..5227cdbad 100644
--- a/go/fory/types.go
+++ b/go/fory/types.go
@@ -17,7 +17,10 @@
package fory
-import "reflect"
+import (
+ "reflect"
+ "strings"
+)
type TypeId = int16
@@ -315,6 +318,7 @@ const (
PrimitiveUint64DispatchId // 17 - uint64 with fixed encoding
PrimitiveFloat32DispatchId // 18
PrimitiveFloat64DispatchId // 19
+ PrimitiveFloat16DispatchId // 20
// ========== NULLABLE DISPATCH IDs ==========
NullableBoolDispatchId
@@ -327,6 +331,7 @@ const (
NullableTaggedInt64DispatchId
NullableFloat32DispatchId
NullableFloat64DispatchId
+ NullableFloat16DispatchId
NullableUint8DispatchId
NullableUint16DispatchId
NullableUint32DispatchId
@@ -350,6 +355,7 @@ const (
UintSliceDispatchId
Float32SliceDispatchId
Float64SliceDispatchId
+ Float16SliceDispatchId
BoolSliceDispatchId
StringSliceDispatchId
@@ -390,6 +396,10 @@ func GetDispatchId(t reflect.Type) DispatchId {
case reflect.Uint8:
return PrimitiveUint8DispatchId
case reflect.Uint16:
+ // Check for fory.Float16 (aliased to uint16)
+ if t.Name() == "Float16" && (t.PkgPath() ==
"github.com/apache/fory/go/fory/float16" || strings.HasSuffix(t.PkgPath(),
"/float16")) {
+ return PrimitiveFloat16DispatchId
+ }
return PrimitiveUint16DispatchId
case reflect.Uint32:
// Default to varint encoding (VAR_UINT32) for xlang
compatibility
@@ -426,6 +436,15 @@ func GetDispatchId(t reflect.Type) DispatchId {
return Float32SliceDispatchId
case reflect.Float64:
return Float64SliceDispatchId
+ case reflect.Uint16:
+ // Check if it's float16 slice
+ if t.Elem().Name() == "Float16" && (t.Elem().PkgPath()
== "github.com/apache/fory/go/fory/float16" ||
strings.HasSuffix(t.Elem().PkgPath(), "/float16")) {
+ return Float16SliceDispatchId
+ }
+ // Use Int16SliceDispatchId for Uint16 as they share
the same 2-byte size
+ // and serialization logic in many cases, or it falls
back to generic if needed.
+ return Int16SliceDispatchId
+
case reflect.Bool:
return BoolSliceDispatchId
case reflect.String:
@@ -480,7 +499,8 @@ func isFixedSizePrimitive(dispatchId DispatchId) bool {
PrimitiveInt16DispatchId, PrimitiveUint16DispatchId,
PrimitiveInt32DispatchId, PrimitiveUint32DispatchId,
PrimitiveInt64DispatchId, PrimitiveUint64DispatchId,
- PrimitiveFloat32DispatchId, PrimitiveFloat64DispatchId:
+ PrimitiveFloat32DispatchId, PrimitiveFloat64DispatchId,
+ PrimitiveFloat16DispatchId:
return true
default:
return false
@@ -495,7 +515,8 @@ func isNullableFixedSizePrimitive(dispatchId DispatchId)
bool {
NullableInt16DispatchId, NullableUint16DispatchId,
NullableInt32DispatchId, NullableUint32DispatchId,
NullableInt64DispatchId, NullableUint64DispatchId,
- NullableFloat32DispatchId, NullableFloat64DispatchId:
+ NullableFloat32DispatchId, NullableFloat64DispatchId,
+ NullableFloat16DispatchId:
return true
default:
return false
@@ -536,7 +557,7 @@ func isPrimitiveDispatchId(dispatchId DispatchId) bool {
case PrimitiveBoolDispatchId, PrimitiveInt8DispatchId,
PrimitiveInt16DispatchId, PrimitiveInt32DispatchId,
PrimitiveInt64DispatchId, PrimitiveIntDispatchId,
PrimitiveUint8DispatchId, PrimitiveUint16DispatchId,
PrimitiveUint32DispatchId, PrimitiveUint64DispatchId,
PrimitiveUintDispatchId,
- PrimitiveFloat32DispatchId, PrimitiveFloat64DispatchId:
+ PrimitiveFloat32DispatchId, PrimitiveFloat64DispatchId,
PrimitiveFloat16DispatchId:
return true
default:
return false
@@ -558,7 +579,7 @@ func isPrimitiveDispatchKind(kind reflect.Kind) bool {
switch kind {
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16,
reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
reflect.Uint64,
- reflect.Float32, reflect.Float64:
+ reflect.Float32, reflect.Float64: // Note: Float16 is uint16
kind, handled by dispatch ID logic
return true
default:
return false
@@ -589,6 +610,8 @@ func getDispatchIdFromTypeId(typeId TypeId, nullable bool)
DispatchId {
return NullableTaggedInt64DispatchId
case FLOAT32:
return NullableFloat32DispatchId
+ case FLOAT16:
+ return NullableFloat16DispatchId
case FLOAT64:
return NullableFloat64DispatchId
case UINT8:
@@ -631,6 +654,8 @@ func getDispatchIdFromTypeId(typeId TypeId, nullable bool)
DispatchId {
return PrimitiveTaggedInt64DispatchId
case FLOAT32:
return PrimitiveFloat32DispatchId
+ case FLOAT16:
+ return PrimitiveFloat16DispatchId
case FLOAT64:
return PrimitiveFloat64DispatchId
case UINT8:
@@ -674,6 +699,8 @@ func getFixedSizeByDispatchId(dispatchId DispatchId) int {
return 2
case PrimitiveInt32DispatchId, PrimitiveUint32DispatchId,
PrimitiveFloat32DispatchId:
return 4
+ case PrimitiveFloat16DispatchId:
+ return 2
case PrimitiveInt64DispatchId, PrimitiveUint64DispatchId,
PrimitiveFloat64DispatchId:
return 8
default:
@@ -721,7 +748,8 @@ func isPrimitiveFixedDispatchId(id DispatchId) bool {
// Fixed-size int32/int64/uint32/uint64 - only when explicitly
specified via TypeId
PrimitiveInt32DispatchId, PrimitiveUint32DispatchId,
PrimitiveInt64DispatchId, PrimitiveUint64DispatchId,
- PrimitiveFloat32DispatchId, PrimitiveFloat64DispatchId:
+ PrimitiveFloat32DispatchId, PrimitiveFloat64DispatchId,
+ PrimitiveFloat16DispatchId:
return true
default:
return false
@@ -737,6 +765,8 @@ func getFixedSizeByPrimitiveDispatchId(id DispatchId) int {
return 2
case PrimitiveInt32DispatchId, PrimitiveUint32DispatchId,
PrimitiveFloat32DispatchId:
return 4
+ case PrimitiveFloat16DispatchId:
+ return 2
case PrimitiveInt64DispatchId, PrimitiveUint64DispatchId,
PrimitiveFloat64DispatchId:
return 8
default:
diff --git a/go/fory/writer.go b/go/fory/writer.go
index e49bca841..27907a587 100644
--- a/go/fory/writer.go
+++ b/go/fory/writer.go
@@ -203,6 +203,9 @@ func (c *WriteContext) writeFast(ptr unsafe.Pointer, ct
DispatchId) {
c.buffer.WriteFloat32(*(*float32)(ptr))
case PrimitiveFloat64DispatchId:
c.buffer.WriteFloat64(*(*float64)(ptr))
+ case PrimitiveFloat16DispatchId:
+ // Float16 is uint16 in Go
+ c.buffer.WriteUint16(*(*uint16)(ptr))
case StringDispatchId:
writeString(c.buffer, *(*string)(ptr))
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]