Skip to content

Commit a89cb77

Browse files
committed
feat(bfloat16): add Phase 5 parse and format functions
Implement BFloat16FromString, fmt.Formatter (supporting %e, %f, %g and variants with width/precision), GoString, MarshalJSON, UnmarshalJSON, MarshalBinary, and UnmarshalBinary. JSON encodes NaN/Inf as strings for round-trip safety. Binary encoding uses 2-byte little-endian.
1 parent eb88d1e commit a89cb77

2 files changed

Lines changed: 448 additions & 0 deletions

File tree

bfloat16_format.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package float16
2+
3+
import (
4+
"encoding/binary"
5+
"encoding/json"
6+
"fmt"
7+
"strconv"
8+
)
9+
10+
// BFloat16FromString parses a string into a BFloat16 value.
11+
// It handles special values (NaN, Inf) and numeric strings.
12+
func BFloat16FromString(s string) (BFloat16, error) {
13+
switch s {
14+
case "NaN":
15+
return BFloat16QuietNaN, nil
16+
case "+Inf", "Inf":
17+
return BFloat16PositiveInfinity, nil
18+
case "-Inf":
19+
return BFloat16NegativeInfinity, nil
20+
case "+0", "0":
21+
return BFloat16PositiveZero, nil
22+
case "-0":
23+
return BFloat16NegativeZero, nil
24+
}
25+
26+
f64, err := strconv.ParseFloat(s, 32)
27+
if err != nil {
28+
return 0, err
29+
}
30+
return BFloat16FromFloat32(float32(f64)), nil
31+
}
32+
33+
// Format implements fmt.Formatter, supporting %e, %f, %g, %E, %F, %G, %v, and %s verbs.
34+
func (b BFloat16) Format(s fmt.State, verb rune) {
35+
switch verb {
36+
case 'e', 'E', 'f', 'F', 'g', 'G':
37+
// Build a format string matching the state flags
38+
format := "%"
39+
if s.Flag('+') {
40+
format += "+"
41+
}
42+
if s.Flag('-') {
43+
format += "-"
44+
}
45+
if s.Flag(' ') {
46+
format += " "
47+
}
48+
if s.Flag('0') {
49+
format += "0"
50+
}
51+
if w, ok := s.Width(); ok {
52+
format += strconv.Itoa(w)
53+
}
54+
if p, ok := s.Precision(); ok {
55+
format += "." + strconv.Itoa(p)
56+
}
57+
format += string(verb)
58+
fmt.Fprintf(s, format, b.ToFloat32())
59+
case 'v':
60+
if s.Flag('#') {
61+
fmt.Fprint(s, b.GoString())
62+
} else {
63+
fmt.Fprint(s, b.String())
64+
}
65+
case 's':
66+
fmt.Fprint(s, b.String())
67+
default:
68+
fmt.Fprintf(s, "%%!%c(bfloat16=%s)", verb, b.String())
69+
}
70+
}
71+
72+
// GoString returns a Go syntax representation of the BFloat16 value.
73+
func (b BFloat16) GoString() string {
74+
return fmt.Sprintf("float16.BFloat16FromBits(0x%04x)", uint16(b))
75+
}
76+
77+
// MarshalJSON implements json.Marshaler.
78+
func (b BFloat16) MarshalJSON() ([]byte, error) {
79+
if b.IsNaN() {
80+
return json.Marshal("NaN")
81+
}
82+
if b.IsInf(1) {
83+
return json.Marshal("+Inf")
84+
}
85+
if b.IsInf(-1) {
86+
return json.Marshal("-Inf")
87+
}
88+
return json.Marshal(b.ToFloat32())
89+
}
90+
91+
// UnmarshalJSON implements json.Unmarshaler.
92+
func (b *BFloat16) UnmarshalJSON(data []byte) error {
93+
// Try as string first (for NaN, Inf)
94+
var s string
95+
if err := json.Unmarshal(data, &s); err == nil {
96+
v, err := BFloat16FromString(s)
97+
if err != nil {
98+
return fmt.Errorf("float16 BFloat16.UnmarshalJSON: invalid string %q", s)
99+
}
100+
*b = v
101+
return nil
102+
}
103+
104+
// Try as number
105+
var f float32
106+
if err := json.Unmarshal(data, &f); err != nil {
107+
return fmt.Errorf("float16 BFloat16.UnmarshalJSON: %w", err)
108+
}
109+
*b = BFloat16FromFloat32(f)
110+
return nil
111+
}
112+
113+
// MarshalBinary implements encoding.BinaryMarshaler.
114+
// The encoding is 2 bytes in little-endian order.
115+
func (b BFloat16) MarshalBinary() ([]byte, error) {
116+
buf := make([]byte, 2)
117+
binary.LittleEndian.PutUint16(buf, uint16(b))
118+
return buf, nil
119+
}
120+
121+
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
122+
// The encoding is 2 bytes in little-endian order.
123+
func (b *BFloat16) UnmarshalBinary(data []byte) error {
124+
if len(data) != 2 {
125+
return fmt.Errorf("float16 BFloat16.UnmarshalBinary: expected 2 bytes, got %d", len(data))
126+
}
127+
*b = BFloat16(binary.LittleEndian.Uint16(data))
128+
return nil
129+
}

0 commit comments

Comments
 (0)