Skip to content

Commit 0d11386

Browse files
committed
Add canonical init pattern following FixedWidthInteger design
IEEE 754 is THE authoritative binary representation for Float/Double, not one encoding among many. This makes it fundamentally different from ASCII (one of many text encodings) and equivalent to FixedWidthInteger (the canonical binary representation for integers). Key insight: IEEE 754 should follow the FixedWidthInteger pattern, not the INCITS 4-1986 ASCII pattern. Changes: - Add unlabeled [UInt8] inits: [UInt8](3.14159) - Add failable inits to Double/Float: Double(bytes: data) - Follow FixedWidthInteger precedent from Standards package - Keep namespace API for explicit IEEE 754 reference - All APIs delegate to authoritative IEEE_754.Binary64/Binary32 Design Philosophy: - FixedWidthInteger: Canonical transformation → unlabeled inits - IEEE 754: Canonical transformation → unlabeled inits (NEW) - ASCII: One-of-many encodings → labeled namespace methods This enables ergonomic usage matching the canonical nature: let bytes = [UInt8](100.0) // Serialization let value = Double(bytes: bytes) // Deserialization While preserving explicit namespace access when needed: let value = Double.ieee754(bytes) let bytes = value.ieee754.bytes() Tests: All 12 tests passing (added 4 canonical init tests)
1 parent 92abf5e commit 0d11386

File tree

4 files changed

+136
-0
lines changed

4 files changed

+136
-0
lines changed

Sources/IEEE_754/Double+IEEE_754.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,35 @@ extension Double {
2525
}
2626
}
2727

28+
// MARK: - Canonical Deserialization
29+
30+
extension Double {
31+
/// Creates Double from IEEE 754 binary64 bytes
32+
///
33+
/// This is the canonical deserialization for Double, following the
34+
/// FixedWidthInteger pattern. IEEE 754 is THE authoritative representation
35+
/// for floating-point values.
36+
///
37+
/// Delegates to `IEEE_754.Binary64.value(from:endianness:)`.
38+
///
39+
/// - Parameters:
40+
/// - bytes: 8-byte array in IEEE 754 binary64 format
41+
/// - endianness: Byte order of input bytes
42+
/// - Returns: nil if bytes.count ≠ 8
43+
///
44+
/// Example:
45+
/// ```swift
46+
/// let value = Double(bytes: data)
47+
/// let value = Double(bytes: data, endianness: .big)
48+
/// ```
49+
public init?(bytes: [UInt8], endianness: [UInt8].Endianness = .little) {
50+
guard let value = IEEE_754.Binary64.value(from: bytes, endianness: endianness) else {
51+
return nil
52+
}
53+
self = value
54+
}
55+
}
56+
2857
// MARK: - Type-level Methods
2958

3059
extension Double {

Sources/IEEE_754/Float+IEEE_754.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,35 @@ extension Float {
2525
}
2626
}
2727

28+
// MARK: - Canonical Deserialization
29+
30+
extension Float {
31+
/// Creates Float from IEEE 754 binary32 bytes
32+
///
33+
/// This is the canonical deserialization for Float, following the
34+
/// FixedWidthInteger pattern. IEEE 754 is THE authoritative representation
35+
/// for floating-point values.
36+
///
37+
/// Delegates to `IEEE_754.Binary32.value(from:endianness:)`.
38+
///
39+
/// - Parameters:
40+
/// - bytes: 4-byte array in IEEE 754 binary32 format
41+
/// - endianness: Byte order of input bytes
42+
/// - Returns: nil if bytes.count ≠ 4
43+
///
44+
/// Example:
45+
/// ```swift
46+
/// let value = Float(bytes: data)
47+
/// let value = Float(bytes: data, endianness: .big)
48+
/// ```
49+
public init?(bytes: [UInt8], endianness: [UInt8].Endianness = .little) {
50+
guard let value = IEEE_754.Binary32.value(from: bytes, endianness: endianness) else {
51+
return nil
52+
}
53+
self = value
54+
}
55+
}
56+
2857
// MARK: - Type-level Methods
2958

3059
extension Float {

Sources/IEEE_754/[UInt8]+IEEE_754.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,45 @@ extension [UInt8].IEEE754 {
106106
IEEE_754.Binary32.value(from: bytes, endianness: endianness)
107107
}
108108
}
109+
110+
// MARK: - Canonical [UInt8] Initializers
111+
112+
extension [UInt8] {
113+
/// Creates byte array from Double using IEEE 754 binary64 format
114+
///
115+
/// This is the canonical binary serialization for Double, following the
116+
/// FixedWidthInteger pattern. IEEE 754 is THE authoritative representation
117+
/// for floating-point values, not one encoding among many.
118+
///
119+
/// - Parameters:
120+
/// - value: Double to serialize
121+
/// - endianness: Byte order (defaults to little-endian)
122+
///
123+
/// Example:
124+
/// ```swift
125+
/// let bytes = [UInt8](3.14159) // Little-endian
126+
/// let bytes = [UInt8](3.14159, endianness: .big) // Big-endian (network order)
127+
/// ```
128+
public init(_ value: Double, endianness: Endianness = .little) {
129+
self = IEEE_754.Binary64.bytes(from: value, endianness: endianness)
130+
}
131+
132+
/// Creates byte array from Float using IEEE 754 binary32 format
133+
///
134+
/// This is the canonical binary serialization for Float, following the
135+
/// FixedWidthInteger pattern. IEEE 754 is THE authoritative representation
136+
/// for floating-point values, not one encoding among many.
137+
///
138+
/// - Parameters:
139+
/// - value: Float to serialize
140+
/// - endianness: Byte order (defaults to little-endian)
141+
///
142+
/// Example:
143+
/// ```swift
144+
/// let bytes = [UInt8](Float(3.14)) // Little-endian
145+
/// let bytes = [UInt8](Float(3.14), endianness: .big) // Big-endian (network order)
146+
/// ```
147+
public init(_ value: Float, endianness: Endianness = .little) {
148+
self = IEEE_754.Binary32.bytes(from: value, endianness: endianness)
149+
}
150+
}

Tests/IEEE_754_Tests/IEEE_754_Tests.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ struct Binary64Tests {
1717
#expect(restored == original)
1818
}
1919

20+
@Test("Double canonical init round-trip")
21+
func doubleCanonicalInitRoundTrip() {
22+
let original: Double = 3.14159265358979323846
23+
let bytes = [UInt8](original)
24+
let restored = Double(bytes: bytes)
25+
26+
#expect(restored == original)
27+
}
28+
2029
@Test("Double big-endian serialization")
2130
func doubleBigEndian() {
2231
let value: Double = 3.14159
@@ -26,6 +35,15 @@ struct Binary64Tests {
2635
#expect(restored == value)
2736
}
2837

38+
@Test("Double canonical init big-endian")
39+
func doubleCanonicalInitBigEndian() {
40+
let value: Double = 3.14159
41+
let bytes = [UInt8](value, endianness: .big)
42+
let restored = Double(bytes: bytes, endianness: .big)
43+
44+
#expect(restored == value)
45+
}
46+
2947
@Test("Double special values")
3048
func doubleSpecialValues() {
3149
// Zero
@@ -53,6 +71,15 @@ struct Binary32Tests {
5371
#expect(restored == original)
5472
}
5573

74+
@Test("Float canonical init round-trip")
75+
func floatCanonicalInitRoundTrip() {
76+
let original: Float = 3.14159
77+
let bytes = [UInt8](original)
78+
let restored = Float(bytes: bytes)
79+
80+
#expect(restored == original)
81+
}
82+
5683
@Test("Float big-endian serialization")
5784
func floatBigEndian() {
5885
let value: Float = 3.14
@@ -62,6 +89,15 @@ struct Binary32Tests {
6289
#expect(restored == value)
6390
}
6491

92+
@Test("Float canonical init big-endian")
93+
func floatCanonicalInitBigEndian() {
94+
let value: Float = 3.14
95+
let bytes = [UInt8](value, endianness: .big)
96+
let restored = Float(bytes: bytes, endianness: .big)
97+
98+
#expect(restored == value)
99+
}
100+
65101
@Test("Float special values")
66102
func floatSpecialValues() {
67103
// Zero

0 commit comments

Comments
 (0)