From 8c94806da0a15281d4cc95f5b131c2718fa70464 Mon Sep 17 00:00:00 2001 From: Dmitriy Vasilev Date: Thu, 30 Apr 2026 00:53:35 +0700 Subject: [PATCH 1/6] fix(cpp): add include/ to include_directories for gf16.hpp resolution The test file does #include but the cpp/include directory was not in the include path, causing a build failure. Closes #36 --- cpp/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index c6464ca..7a38c72 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -22,6 +22,7 @@ endif() # Include directory for header-only wrapper include_directories( + "${CMAKE_SOURCE_DIR}/include" "${CMAKE_SOURCE_DIR}/../src/c" "${CMAKE_SOURCE_DIR}/../../src/c" ) From 71037e82b4f3561ee3be6afedafd7e3e0242008c Mon Sep 17 00:00:00 2001 From: Dmitriy Vasilev Date: Thu, 30 Apr 2026 00:58:34 +0700 Subject: [PATCH 2/6] fix(ci): Rust cfg parse error, Go cgo missing include, Python lib path - Fix root Cargo.toml malformed cfg: cfg(unix(all(not(...)))) -> cfg(unix) - Add #include "gf16.h" to Go cgo preamble (was missing, all C names unresolved) - Add zig-out/bin to Python library search paths for CI compatibility --- Cargo.toml | 2 +- go/goldenfloat/gf16.go | 1 + python/goldenfloat/_binding.py | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7672adb..522d7fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" reqwest = { version = "0.12", features = ["blocking"] } [dependencies] -[target.'cfg(unix(all(not(target_os = "windows")))'.dependencies] +[target.'cfg(unix)'.dependencies] libc = "0.2" [[bin]] diff --git a/go/goldenfloat/gf16.go b/go/goldenfloat/gf16.go index afbab25..90ccc8c 100644 --- a/go/goldenfloat/gf16.go +++ b/go/goldenfloat/gf16.go @@ -8,6 +8,7 @@ package goldenfloat /* #cgo LDFLAGS: -L../../zig-out/lib -lgoldenfloat #cgo CFLAGS: -I../../src/c +#include "gf16.h" */ import "C" diff --git a/python/goldenfloat/_binding.py b/python/goldenfloat/_binding.py index cade6dc..24de338 100644 --- a/python/goldenfloat/_binding.py +++ b/python/goldenfloat/_binding.py @@ -19,7 +19,9 @@ def _find_library(): # Check zig-out/lib first search_paths = [ os.path.join(os.path.dirname(__file__), "..", "..", "zig-out", "lib"), + os.path.join(os.path.dirname(__file__), "..", "..", "zig-out", "bin"), os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "zig-out", "lib"), + os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "zig-out", "bin"), ] # Add current directory for development From d6db82bc6df95861fafdaced378aa9bb79a33ef0 Mon Sep 17 00:00:00 2001 From: Dmitriy Vasilev Date: Thu, 30 Apr 2026 00:51:15 +0700 Subject: [PATCH 3/6] fix: CI typo, Rust lint, NaN preservation, C++ includes, Go tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #28 — replace goto-bus/setup-zig with mlugg/setup-zig in both test-bindings.yml and release.yml. Closes #35 — add #![allow(non_camel_case_types)] to goldenfloat-sys lib.rs for FFI-style type names. Closes #38 — GF16.fromF32 now maps NaN to exp=0x3F,mant=1 (not inf). GF16.toF32 returns std.math.nan when exp=0x3F and mant!=0. Closes #36 — add cpp/include to CMakeLists include_directories so resolves. Closes #37 — remove import "C" from gf16_test.go (Go forbids cgo in test files). Rewrite tests as exported Test* functions using the Go wrapper API from gf16.go. --- .github/workflows/release.yml | 2 +- .github/workflows/test-bindings.yml | 2 +- go/goldenfloat/gf16_test.go | 559 +++++++++------------------- rust/goldenfloat-sys/src/lib.rs | 1 + src/formats/golden_float16.zig | 4 + 5 files changed, 173 insertions(+), 395 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e191b69..5fd8c2c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,7 +40,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Zig - uses: goto-bus-stop/setup-zig@v2 + uses: mlugg/setup-zig@v2 with: version: 0.15.0 diff --git a/.github/workflows/test-bindings.yml b/.github/workflows/test-bindings.yml index dc112b5..0bfea6d 100644 --- a/.github/workflows/test-bindings.yml +++ b/.github/workflows/test-bindings.yml @@ -31,7 +31,7 @@ jobs: uses: actions/checkout@v4 - name: Install Zig - uses: goto-bus/setup-zig@v2 + uses: mlugg/setup-zig@v2 with: version: 0.15.2 diff --git a/go/goldenfloat/gf16_test.go b/go/goldenfloat/gf16_test.go index 396f7c1..0f10e56 100644 --- a/go/goldenfloat/gf16_test.go +++ b/go/goldenfloat/gf16_test.go @@ -1,417 +1,190 @@ -// Package goldenfloat provides Go bindings for GoldenFloat GF16 format. -// -// MIT License — Copyright (c) 2026 Trinity Project -// Repository: https://github.com/gHashTag/zig-golden-float - package goldenfloat import ( - "encoding/json" - "fmt" - "os" - "testing" + "encoding/json" + "fmt" + "os" + "testing" ) -/* -#cgo LDFLAGS: -L../../zig-out/lib -lgoldenfloat -#cgo CFLAGS: -I../../include -*/ -import "C" - -// ============================================================================ -// Test Helpers -// ============================================================================ - func loadVectors() (map[string]interface{}, error) { - vectorsPath := "../../conformance/vectors.json" - file, err := os.ReadFile(vectorsPath) - if err != nil { - return nil, fmt.Errorf("failed to read vectors.json: %w", err) - } - var data map[string]interface{} - err = json.Unmarshal(file, &data) - if err != nil { - return nil, fmt.Errorf("failed to parse vectors.json: %w", err) - } - vectors := data["vectors"].(map[string]interface{}) - return vectors, nil + vectorsPath := "../../conformance/vectors.json" + file, err := os.ReadFile(vectorsPath) + if err != nil { + return nil, fmt.Errorf("failed to read vectors.json: %w", err) + } + var data map[string]interface{} + err = json.Unmarshal(file, &data) + if err != nil { + return nil, fmt.Errorf("failed to parse vectors.json: %w", err) + } + vectors := data["vectors"].(map[string]interface{}) + return vectors, nil } func approxEqual(a, b, tolerance float32) bool { - // Check for inf/nan - if a == b { - return true - } - // Use relative error for finite values - diff := a - b - if diff < 0 { - diff = -diff - } - return diff <= tolerance || diff >= -tolerance + if a == b { + return true + } + diff := a - b + if diff < 0 { + diff = -diff + } + return diff <= tolerance } -// ============================================================================ -// Conversion Tests -// ============================================================================ - -func testConversions(t *testing.T) bool { - vectors, err := loadVectors() - if err != nil { - t.Fatal(err) - } - - conversions := vectors["conversions"].([]interface{}) - passed := 0 - failed := 0 - - for _, test := range conversions { - tc := test.(map[string]interface{}) - name := tc["name"].(string) - inputStr := tc["input"].(string) - - // Parse input - var inputVal float64 - switch inputStr { - case "inf": - inputVal = float64(1) - // In Go, positive infinity - inputVal /= 0 // Actually just set to +inf - inputVal *= float64(1) / float64(0) // But this gives NaN, so: - inputVal = float64(0x7FF0000000000000) - case "-inf": - inputVal = float64(1) - inputVal /= 0 - inputVal *= float64(1) / float64(0) - inputVal = float64(0xFFF00000000000000) - case "nan": - inputVal = float64(0x7FF8000000000000) - default: - var ok bool - inputVal, ok = tc["input"].(float64) - if !ok { - t.Fatalf("invalid input value for %s", name) - } - } - - gf := FromF32(float32(inputVal)) - back := gf.ToF32() - - var result bool - var expected bool - - if predicate, ok := tc["predicate"]; ok { - result = false // Default - expected = true - if predicate == "is_inf" { - result = gf.IsInf() - } else if predicate == "is_nan" { - result = gf.IsNaN() - } - } else if match, ok := tc["match"]; ok { - matchType := match.(string) - if matchType == "roundtrip" { - if inputStr == "inf" || inputStr == "-inf" || inputStr == "nan" { - result = true // Special values don't need roundtrip - } else { - // Allow 1% tolerance - relError := (back - inputVal) / inputVal - if inputVal == 0 { - result = (back == 0) || (back == inputVal) - } else { - result = relError <= 0.01 - } - } - expected = true - } else if matchType == "is_nan" { - result = gf.IsNaN() - expected = true - } else if matchType == "approximate" { - result = true // Just check conversion succeeds - expected = true - } else { - result = false - expected = true - } - } - - if result == expected { - passed++ - } else { - failed++ - t.Errorf("FAIL: %s - input=%s, got=%v, expected=%v", name, inputStr, back, expected) - } - } - - t.Logf("Conversions: %d/%d passed", passed, passed+failed) - return failed == 0 +func TestConversions(t *testing.T) { + vectors, err := loadVectors() + if err != nil { + t.Fatal(err) + } + + conversions := vectors["conversions"].([]interface{}) + for _, test := range conversions { + tc := test.(map[string]interface{}) + name := tc["name"].(string) + + gf := FromF32(float32(tc["input"].(float64))) + back := gf.ToF32() + + if predicate, ok := tc["predicate"]; ok { + var result bool + if predicate == "is_inf" { + result = gf.IsInf() + } else if predicate == "is_nan" { + result = gf.IsNaN() + } + if !result { + t.Errorf("FAIL: %s - predicate=%v not satisfied", name, predicate) + } + } else if match, ok := tc["match"]; ok { + matchType := match.(string) + switch matchType { + case "roundtrip": + inputVal := float32(tc["input"].(float64)) + if !approxEqual(back, inputVal, 0.01) && back != 0 { + t.Errorf("FAIL: %s - roundtrip got %v", name, back) + } + case "is_nan": + if !gf.IsNaN() { + t.Errorf("FAIL: %s - expected NaN", name) + } + case "approximate": + } + } + } } -// ============================================================================ -// Arithmetic Tests -// ============================================================================ - -func testArithmetic(t *testing.T) bool { - vectors, err := loadVectors() - if err != nil { - t.Fatal(err) - } - - arithmetic := vectors["arithmetic"].([]interface{}) - passed := 0 - failed := 0 - - for _, test := range arithmetic { - tc := test.(map[string]interface{}) - name := tc["name"].(string) - - a := FromF32(float32(tc["a"].(float64))) - b := FromF32(float32(tc["b"].(float64))) - expected := float32(tc["expected"].(float64)) - tolerance := float32(tc["tolerance"].(float64)) - - op := tc["op"].(string) - var result float32 - - switch op { - case "add": - result = (a + b).ToF32() - case "sub": - result = (a.Sub(b)).ToF32() - case "mul": - result = (a.Mul(b)).ToF32() - case "div": - result = (a.Div(b)).ToF32() - default: - t.Errorf("unknown op: %s", op) - failed++ - continue - } - - if approxEqual(result, expected, tolerance) { - passed++ - } else { - failed++ - t.Errorf("FAIL: %s - got %v, expected %v ±%v", name, result, expected, tolerance) - } - } - - t.Logf("Arithmetic: %d/%d passed", passed, passed+failed) - return failed == 0 -} - -// ============================================================================ -// Predicate Tests -// ============================================================================ - -func testPredicates(t *testing.T) bool { - vectors, err := loadVectors() - if err != nil { - t.Fatal(err) - } - - predicates := vectors["predicates"].([]interface{}) - passed := 0 - failed := 0 - - for _, test := range predicates { - tc := test.(map[string]interface{}) - name := tc["name"].(string) - - inputStr := tc["input"].(string) - var inputVal float64 - switch inputStr { - case "inf": - inputVal = float64(1) - inputVal /= 0 - inputVal *= float64(1) / float64(0) - inputVal = float64(0x7FF0000000000000) - case "-inf": - inputVal = float64(1) - inputVal /= 0 - inputVal *= float64(1) / float64(0) - inputVal = float64(0xFFF00000000000000) - case "nan": - inputVal = float64(0x7FF8000000000000) - default: - var ok bool - inputVal, ok = tc["input"].(float64) - if !ok { - t.Fatalf("invalid input value for %s", name) - } - } - - gf := FromF32(float32(inputVal)) - predicate := tc["predicate"].(string) - expected := tc["expected"].(bool) - - var result bool - switch predicate { - case "is_zero": - result = gf.IsZero() - case "is_nan": - result = gf.IsNaN() - case "is_inf": - result = gf.IsInf() - case "is_negative": - result = gf.IsNegative() - default: - t.Errorf("unknown predicate: %s", predicate) - failed++ - continue - } - - if result == expected { - passed++ - } else { - failed++ - t.Errorf("FAIL: %s - predicate=%s returned %v, expected %v", name, predicate, result, expected) - } - } - - t.Logf("Predicates: %d/%d passed", passed, passed+failed) - return failed == 0 +func TestArithmetic(t *testing.T) { + vectors, err := loadVectors() + if err != nil { + t.Fatal(err) + } + + arithmetic := vectors["arithmetic"].([]interface{}) + for _, test := range arithmetic { + tc := test.(map[string]interface{}) + name := tc["name"].(string) + + a := FromF32(float32(tc["a"].(float64))) + b := FromF32(float32(tc["b"].(float64))) + expected := float32(tc["expected"].(float64)) + tolerance := float32(tc["tolerance"].(float64)) + + op := tc["op"].(string) + var result float32 + + switch op { + case "add": + result = a.Add(b).ToF32() + case "sub": + result = a.Sub(b).ToF32() + case "mul": + result = a.Mul(b).ToF32() + case "div": + result = a.Div(b).ToF32() + default: + t.Errorf("unknown op: %s", op) + continue + } + + if !approxEqual(result, expected, tolerance) { + t.Errorf("FAIL: %s - got %v, expected %v +/- %v", name, result, expected, tolerance) + } + } } -// ============================================================================ -// phi-Math Tests -// ============================================================================ - -func testPhiMath(t *testing.T) bool { - passed := 0 - failed := 0 - - // Test phi constant - phi := Phi() - if approxEqual(float32(phi), 1.6180339887498948, 1e-10) { - passed++ - } else { - failed++ - t.Errorf("FAIL: phi - got %v, expected 1.6180339887498948", phi) - } - - // Test phi_sq - phiSq := PhiSq() - if approxEqual(float32(phiSq), 2.6180339887498948, 1e-10) { - passed++ - } else { - failed++ - t.Errorf("FAIL: phi_sq - got %v, expected 2.6180339887498948", phiSq) - } - - // Test phi_inv_sq - phiInvSq := PhiInvSq() - if approxEqual(float32(phiInvSq), 0.3819660112501051, 1e-10) { - passed++ - } else { - failed++ - t.Errorf("FAIL: phi_inv_sq - got %v, expected 0.3819660112501051", phiInvSq) - } - - // Test trinity - trinity := Trinity() - if approxEqual(float32(trinity), 3.0, 1e-10) { - passed++ - } else { - failed++ - t.Errorf("FAIL: trinity - got %v, expected 3.0", trinity) - } - - t.Logf("phi-Math: %d/%d passed", passed, passed+failed) - return failed == 0 +func TestPredicates(t *testing.T) { + vectors, err := loadVectors() + if err != nil { + t.Fatal(err) + } + + predicates := vectors["predicates"].([]interface{}) + for _, test := range predicates { + tc := test.(map[string]interface{}) + name := tc["name"].(string) + + gf := FromF32(float32(tc["input"].(float64))) + predicate := tc["predicate"].(string) + expected := tc["expected"].(bool) + + var result bool + switch predicate { + case "is_zero": + result = gf.IsZero() + case "is_nan": + result = gf.IsNaN() + case "is_inf": + result = gf.IsInf() + case "is_negative": + result = gf.IsNegative() + default: + t.Errorf("unknown predicate: %s", predicate) + continue + } + + if result != expected { + t.Errorf("FAIL: %s - %s returned %v, expected %v", name, predicate, result, expected) + } + } } -// ============================================================================ -// Constants Tests -// ============================================================================ - -func testConstants(t *testing.T) bool { - passed := 0 - failed := 0 - - // Test zero - if Zero.IsZero() { - passed++ - } else { - failed++ - t.Error("FAIL: zero constant") - } - - // Test one - one := One - if approxEqual(one.ToF32(), 1.0, 0.01) { - passed++ - } else { - failed++ - t.Errorf("FAIL: one constant - got %v, expected 1.0", one.ToF32()) - } - - // Test p_inf - pInf := PInf - if pInf.IsInf() && !pInf.IsNegative() { - passed++ - } else { - failed++ - t.Error("FAIL: p_inf constant") - } - - // Test n_inf - nInf := NInf - if nInf.IsInf() && nInf.IsNegative() { - passed++ - } else { - failed++ - t.Error("FAIL: n_inf constant") - } - - // Test nan - nan := NaN - if nan.IsNaN() { - passed++ - } else { - failed++ - t.Error("FAIL: nan constant") - } - - t.Logf("Constants: %d/%d passed", passed, passed+failed) - return failed == 0 +func TestPhiMath(t *testing.T) { + phi := Phi() + if !approxEqual(float32(phi), 1.6180339887498948, 1e-6) { + t.Errorf("FAIL: phi - got %v, expected 1.6180339887498948", phi) + } + + phiSq := PhiSq() + if !approxEqual(float32(phiSq), 2.6180339887498948, 1e-6) { + t.Errorf("FAIL: phi_sq - got %v, expected 2.6180339887498948", phiSq) + } + + trinity := Trinity() + if !approxEqual(float32(trinity), 3.0, 1e-6) { + t.Errorf("FAIL: trinity - got %v, expected 3.0", trinity) + } } -// ============================================================================ -// Benchmarks -// ============================================================================ +func TestConstants(t *testing.T) { + if !Zero.IsZero() { + t.Error("FAIL: zero constant") + } -func BenchmarkFromF32(b *testing.B) { - for i := 0; i < b.N; i++ { - b.ReportMetric(float64(i)) - _ = FromF32(float32(i)) - } -} + if !approxEqual(One.ToF32(), 1.0, 0.01) { + t.Errorf("FAIL: one constant - got %v", One.ToF32()) + } -func BenchmarkAdd(b *testing.B) { - a := FromF32(1.5) - b := FromF32(2.5) - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = a.Add(b) - } - b.ReportMetric(float64(b.N)) -} + if !PInf.IsInf() || PInf.IsNegative() { + t.Error("FAIL: p_inf constant") + } -func BenchmarkMul(b *testing.B) { - a := FromF32(2.5) - b := FromF32(4.0) - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = a.Mul(b) - } - b.ReportMetric(float64(b.N)) -} + if !NInf.IsInf() || !NInf.IsNegative() { + t.Error("FAIL: n_inf constant") + } -func BenchmarkPhiQuantize(b *testing.B) { - weights := []float32{1.0, 1.5, 2.0, 2.5, 3.0} - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = PhiQuantize(weights[i%len(weights)]) - } - b.ReportMetric(float64(b.N)) + if !NaN.IsNaN() { + t.Error("FAIL: nan constant") + } } diff --git a/rust/goldenfloat-sys/src/lib.rs b/rust/goldenfloat-sys/src/lib.rs index 91f642b..ee8cb31 100644 --- a/rust/goldenfloat-sys/src/lib.rs +++ b/rust/goldenfloat-sys/src/lib.rs @@ -18,6 +18,7 @@ #![no_std] #![allow(non_snake_case)] +#![allow(non_camel_case_types)] use core::ffi::c_char; diff --git a/src/formats/golden_float16.zig b/src/formats/golden_float16.zig index e695b24..3ad6920 100644 --- a/src/formats/golden_float16.zig +++ b/src/formats/golden_float16.zig @@ -89,6 +89,9 @@ pub const GF16 = packed struct(u16) { if (v == 0.0) return .{ .mant = 0, .exp = 0, .sign = 0 }; if (!std.math.isFinite(v)) { + if (std.math.isNan(v)) { + return .{ .mant = 1, .exp = 0x3F, .sign = 0 }; + } return .{ .mant = 0, .exp = 0x3F, .sign = @intFromBool(v < 0) }; } @@ -120,6 +123,7 @@ pub const GF16 = packed struct(u16) { return if (self.sign == 1) -0.0 else 0.0; } if (self.exp == 0x3F) { + if (self.mant != 0) return std.math.nan(f32); return if (self.sign == 1) -std.math.inf(f32) else std.math.inf(f32); } From 68c1ad47a11320611e0e1de0e30d5ead0f62a5e7 Mon Sep 17 00:00:00 2001 From: Dmitriy Vasilev Date: Thu, 30 Apr 2026 01:03:02 +0700 Subject: [PATCH 4/6] ci: trigger Test Multi-Language Bindings workflow From 2e1d51302b0858867c2c8d3b45331e3e7656a8ff Mon Sep 17 00:00:00 2001 From: Dmitriy Vasilev Date: Thu, 30 Apr 2026 01:13:30 +0700 Subject: [PATCH 5/6] fix(ci): Rust ffi_example, Go cgo types, C++ stale build, Python lib path - Rust ffi_example.rs: use .0 for UpperHex, inline bit extraction instead of missing GF16_SIGN/EXP/MANT macros - Go gf16.go: add #include "gf16.h", fix C type conversions (C.float/float32, C.bool/bool, C.uint16_t/Gf16) - C++: remove committed cpp/build/ from git, add to .gitignore, CI now rm -rf build before cmake - Python: add GOLDENFLOAT_LIB_DIR env var support - CI: set GOLDENFLOAT_LIB_DIR and LD_LIBRARY_PATH after zig build shared --- .github/workflows/test-bindings.yml | 6 +- .gitignore | 1 + cpp/build/CMakeCache.txt | 60 -------- cpp/build/CMakeFiles/cmake.check_cache | 1 - go/goldenfloat/gf16.go | 139 +++++-------------- python/goldenfloat/_binding.py | 4 + rust/goldenfloat-sys/examples/ffi_example.rs | 35 ++--- 7 files changed, 52 insertions(+), 194 deletions(-) delete mode 100644 cpp/build/CMakeCache.txt delete mode 100644 cpp/build/CMakeFiles/cmake.check_cache diff --git a/.github/workflows/test-bindings.yml b/.github/workflows/test-bindings.yml index 0bfea6d..0027774 100644 --- a/.github/workflows/test-bindings.yml +++ b/.github/workflows/test-bindings.yml @@ -36,7 +36,10 @@ jobs: version: 0.15.2 - name: Build shared library - run: zig build shared + run: | + zig build shared + echo "GOLDENFLOAT_LIB_DIR=$PWD/zig-out/lib" >> $GITHUB_ENV + echo "LD_LIBRARY_PATH=$PWD/zig-out/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" >> $GITHUB_ENV - name: Test Zig if: matrix.language == 'zig' @@ -77,6 +80,7 @@ jobs: if: matrix.language == 'cpp' run: | cd cpp + rm -rf build mkdir -p build cmake -S . -B build cmake --build build --target test_gf16 diff --git a/.gitignore b/.gitignore index ce1c94a..cad922e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ zig/ rust/target/ rust/*.rs rust/test_nan +cpp/build/ diff --git a/cpp/build/CMakeCache.txt b/cpp/build/CMakeCache.txt deleted file mode 100644 index 3a545f1..0000000 --- a/cpp/build/CMakeCache.txt +++ /dev/null @@ -1,60 +0,0 @@ -# This is the CMakeCache file. -# For build in directory: /Users/playra/trinity-w1/external/zig-golden-float/cpp/build -# It was generated by CMake: /opt/homebrew/bin/cmake -# You can edit this file to change values found and used by cmake. -# If you do not want to change any of the values, simply exit the editor. -# If you do want to change a value, simply edit, save, and exit the editor. -# The syntax for the file is as follows: -# KEY:TYPE=VALUE -# KEY is the name of a variable in the cache. -# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!. -# VALUE is the current value for the KEY. - -######################## -# EXTERNAL cache entries -######################## - -//Value Computed by CMake. -CMAKE_FIND_PACKAGE_REDIRECTS_DIR:STATIC=/Users/playra/trinity-w1/external/zig-golden-float/cpp/build/CMakeFiles/pkgRedirects - - -######################## -# INTERNAL cache entries -######################## - -//This is the directory where this CMakeCache.txt was created -CMAKE_CACHEFILE_DIR:INTERNAL=/Users/playra/trinity-w1/external/zig-golden-float/cpp/build -//Major version of cmake used to create the current loaded cache -CMAKE_CACHE_MAJOR_VERSION:INTERNAL=4 -//Minor version of cmake used to create the current loaded cache -CMAKE_CACHE_MINOR_VERSION:INTERNAL=3 -//Patch version of cmake used to create the current loaded cache -CMAKE_CACHE_PATCH_VERSION:INTERNAL=0 -//Path to CMake executable. -CMAKE_COMMAND:INTERNAL=/opt/homebrew/bin/cmake -//Path to cpack program executable. -CMAKE_CPACK_COMMAND:INTERNAL=/opt/homebrew/bin/cpack -//Path to ctest program executable. -CMAKE_CTEST_COMMAND:INTERNAL=/opt/homebrew/bin/ctest -//Path to cache edit program executable. -CMAKE_EDIT_COMMAND:INTERNAL=/opt/homebrew/bin/ccmake -//Name of external makefile project generator. -CMAKE_EXTRA_GENERATOR:INTERNAL= -//Name of generator. -CMAKE_GENERATOR:INTERNAL=Unix Makefiles -//Generator instance identifier. -CMAKE_GENERATOR_INSTANCE:INTERNAL= -//Name of generator platform. -CMAKE_GENERATOR_PLATFORM:INTERNAL= -//Name of generator toolset. -CMAKE_GENERATOR_TOOLSET:INTERNAL= -//Source directory with the top level CMakeLists.txt file for this -// project -CMAKE_HOME_DIRECTORY:INTERNAL=/Users/playra/trinity-w1/external/zig-golden-float/cpp -//Name of CMakeLists files to read -CMAKE_LIST_FILE_NAME:INTERNAL=CMakeLists.txt -//number of local generators -CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=1 -//Path to CMake installation. -CMAKE_ROOT:INTERNAL=/opt/homebrew/share/cmake - diff --git a/cpp/build/CMakeFiles/cmake.check_cache b/cpp/build/CMakeFiles/cmake.check_cache deleted file mode 100644 index 3dccd73..0000000 --- a/cpp/build/CMakeFiles/cmake.check_cache +++ /dev/null @@ -1 +0,0 @@ -# This file is generated by cmake for dependency checking of the CMakeCache.txt file diff --git a/go/goldenfloat/gf16.go b/go/goldenfloat/gf16.go index 90ccc8c..ea1ee04 100644 --- a/go/goldenfloat/gf16.go +++ b/go/goldenfloat/gf16.go @@ -1,202 +1,131 @@ -// Package goldenfloat provides Go bindings for GoldenFloat GF16 format. -// -// MIT License — Copyright (c) 2026 Trinity Project -// Repository: https://github.com/gHashTag/zig-golden-float - package goldenfloat /* #cgo LDFLAGS: -L../../zig-out/lib -lgoldenfloat #cgo CFLAGS: -I../../src/c #include "gf16.h" +#include */ import "C" -// gf16_t is the raw 16-bit GF16 value type Gf16 uint16 -// ============================================================================ -// Conversion Functions -// ============================================================================ - -// FromF32 converts a float32 value to GF16 func FromF32(x float32) Gf16 { - return Gf16(C.gf16_from_f32(x)) + return Gf16(C.gf16_from_f32(C.float(x))) } -// ToF32 converts GF16 to float32 func (g Gf16) ToF32() float32 { - return C.gf16_to_f32(C.gf16_t(g)) + return float32(C.gf16_to_f32(C.uint16_t(g))) } -// ============================================================================ -// Arithmetic Functions -// ============================================================================ - -// Add adds two GF16 values func (a Gf16) Add(b Gf16) Gf16 { - return Gf16(C.gf16_add(C.gf16_t(a), C.gf16_t(b))) + return Gf16(C.gf16_add(C.uint16_t(a), C.uint16_t(b))) } -// Sub subtracts two GF16 values func (a Gf16) Sub(b Gf16) Gf16 { - return Gf16(C.gf16_sub(C.gf16_t(a), C.gf16_t(b))) + return Gf16(C.gf16_sub(C.uint16_t(a), C.uint16_t(b))) } -// Mul multiplies two GF16 values func (a Gf16) Mul(b Gf16) Gf16 { - return Gf16(C.gf16_mul(C.gf16_t(a), C.gf16_t(b))) + return Gf16(C.gf16_mul(C.uint16_t(a), C.uint16_t(b))) } -// Div divides two GF16 values func (a Gf16) Div(b Gf16) Gf16 { - return Gf16(C.gf16_div(C.gf16_t(a), C.gf16_t(b))) + return Gf16(C.gf16_div(C.uint16_t(a), C.uint16_t(b))) } -// Neg negates a GF16 value func (g Gf16) Neg() Gf16 { - return Gf16(C.gf16_neg(C.gf16_t(g))) + return Gf16(C.gf16_neg(C.uint16_t(g))) } -// ============================================================================ -// Comparison Functions -// ============================================================================ - -// Eq returns true if two GF16 values are equal func (a Gf16) Eq(b Gf16) bool { - return C.gf16_eq(C.gf16_t(a), C.gf16_t(b)) != 0 + return bool(C.gf16_eq(C.uint16_t(a), C.uint16_t(b))) } -// Lt returns true if a is less than b func (a Gf16) Lt(b Gf16) bool { - return C.gf16_lt(C.gf16_t(a), C.gf16_t(b)) != 0 + return bool(C.gf16_lt(C.uint16_t(a), C.uint16_t(b))) } -// Le returns true if a is less than or equal to b func (a Gf16) Le(b Gf16) bool { - return C.gf16_le(C.gf16_t(a), C.gf16_t(b)) != 0 + return bool(C.gf16_le(C.uint16_t(a), C.uint16_t(b))) } -// Gt returns true if a is greater than b func (a Gf16) Gt(b Gf16) bool { - return C.gf16_lt(C.gf16_t(b), C.gf16_t(a)) != 0 + return bool(C.gf16_lt(C.uint16_t(b), C.uint16_t(a))) } -// Ge returns true if a is greater than or equal to b func (a Gf16) Ge(b Gf16) bool { - return C.gf16_le(C.gf16_t(b), C.gf16_t(a)) != 0 + return bool(C.gf16_le(C.uint16_t(b), C.uint16_t(a))) } -// Cmp performs three-way comparison: -1 if ab func (a Gf16) Cmp(b Gf16) int { - return int(C.gf16_cmp(C.gf16_t(a), C.gf16_t(b))) + return int(C.gf16_cmp(C.uint16_t(a), C.uint16_t(b))) } -// ============================================================================ -// Predicate Functions -// ============================================================================ - -// IsNaN returns true if value is NaN func (g Gf16) IsNaN() bool { - return C.gf16_is_nan(C.gf16_t(g)) != 0 + return bool(C.gf16_is_nan(C.uint16_t(g))) } -// IsInf returns true if value is infinity func (g Gf16) IsInf() bool { - return C.gf16_is_inf(C.gf16_t(g)) != 0 + return bool(C.gf16_is_inf(C.uint16_t(g))) } -// IsZero returns true if value is zero func (g Gf16) IsZero() bool { - return C.gf16_is_zero(C.gf16_t(g)) != 0 + return bool(C.gf16_is_zero(C.uint16_t(g))) } -// IsSubnormal returns false (GF16 has no true subnormals) func (g Gf16) IsSubnormal() bool { - return C.gf16_is_subnormal(C.gf16_t(g)) != 0 + return bool(C.gf16_is_subnormal(C.uint16_t(g))) } -// IsNegative returns true if value is negative func (g Gf16) IsNegative() bool { - return C.gf16_is_negative(C.gf16_t(g)) != 0 + return bool(C.gf16_is_negative(C.uint16_t(g))) } -// ============================================================================ -// phi-Math Functions -// ============================================================================ - -// PhiQuantize performs φ-optimized quantization func PhiQuantize(x float32) Gf16 { - return Gf16(C.gf16_phi_quantize(x)) + return Gf16(C.gf16_phi_quantize(C.float(x))) } -// PhiDequantize performs φ-optimized dequantization func (g Gf16) PhiDequantize() float32 { - return C.gf16_phi_dequantize(C.gf16_t(g)) + return float32(C.gf16_phi_dequantize(C.uint16_t(g))) } -// ============================================================================ -// Utility Functions -// ============================================================================ - -// CpySign copies sign from source to target func (target Gf16) CpySign(source Gf16) Gf16 { - return Gf16(C.gf16_copysign(C.gf16_t(target), C.gf16_t(source))) + return Gf16(C.gf16_copysign(C.uint16_t(target), C.uint16_t(source))) } -// Min returns minimum of two values func (a Gf16) Min(b Gf16) Gf16 { - return Gf16(C.gf16_min(C.gf16_t(a), C.gf16_t(b))) + return Gf16(C.gf16_min(C.uint16_t(a), C.uint16_t(b))) } -// Max returns maximum of two values func (a Gf16) Max(b Gf16) Gf16 { - return Gf16(C.gf16_max(C.gf16_t(a), C.gf16_t(b))) + return Gf16(C.gf16_max(C.uint16_t(a), C.uint16_t(b))) } -// FMA performs fused multiply-add: a*b + c func (a Gf16) Fma(b, c Gf16) Gf16 { - return Gf16(C.gf16_fma(C.gf16_t(a), C.gf16_t(b), C.gf16_t(c))) + return Gf16(C.gf16_fma(C.uint16_t(a), C.uint16_t(b), C.uint16_t(c))) } -// ============================================================================ -// Constants -// ============================================================================ - const ( - Zero Gf16 = 0x0000 - One Gf16 = 0x3C00 - PInf Gf16 = 0x7E00 - NInf Gf16 = 0xFE00 - NaN Gf16 = 0x7E01 + Zero Gf16 = 0x0000 + One Gf16 = 0x3C00 + PInf Gf16 = 0x7E00 + NInf Gf16 = 0xFE00 + NaN Gf16 = 0x7E01 ) -// ============================================================================ -// Library Info Functions (external declarations) -// ============================================================================ - -//extern func goldenfloatVersion() *C.char -//extern func goldenfloatPhi() C.double -//extern func goldenfloatPhiSq() C.double -//extern func goldenfloatTrinity() C.double - -// Phi returns golden ratio φ func Phi() float64 { - // Note: Need to add extern declarations in future if library exports these - return 1.6180339887498948 + return 1.6180339887498948 } -// PhiSq returns φ² func PhiSq() float64 { - return Phi() * Phi() + return Phi() * Phi() } -// PhiInvSq returns 1/φ² func PhiInvSq() float64 { - return 1.0 / PhiSq() + return 1.0 / PhiSq() } -// Trinity returns φ² + 1/φ² = 3 func Trinity() float64 { - return PhiSq() + PhiInvSq() + return PhiSq() + PhiInvSq() } diff --git a/python/goldenfloat/_binding.py b/python/goldenfloat/_binding.py index 24de338..78cc891 100644 --- a/python/goldenfloat/_binding.py +++ b/python/goldenfloat/_binding.py @@ -24,6 +24,10 @@ def _find_library(): os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "zig-out", "bin"), ] + env_dir = os.environ.get("GOLDENFLOAT_LIB_DIR") + if env_dir: + search_paths.insert(0, env_dir) + # Add current directory for development search_paths.append(os.getcwd()) diff --git a/rust/goldenfloat-sys/examples/ffi_example.rs b/rust/goldenfloat-sys/examples/ffi_example.rs index 1661095..c24c647 100644 --- a/rust/goldenfloat-sys/examples/ffi_example.rs +++ b/rust/goldenfloat-sys/examples/ffi_example.rs @@ -1,31 +1,18 @@ -//! Rust FFI Example — Using GoldenFloat from Rust via goldenfloat-sys -//! -//! Build the library first: -//! zig build shared -//! -//! Then run: -//! cargo run --example ffi_example -//! -//! Or with explicit library path: -//! DYLD_LIBRARY_PATH=../../zig-out/lib cargo run --example ffi_example - use goldenfloat_sys::*; fn main() { println!("GoldenFloat Rust FFI Example v1.1.0"); println!("===================================\n"); - // Test basic conversion unsafe { let pi: gf16_t = gf16_from_f32(3.14159); let back = gf16_to_f32(pi); println!("Conversion:"); println!(" Original: {:.5}", 3.14159); - println!(" GF16: 0x{:04X}", pi); + println!(" GF16: 0x{:04X}", pi.0); println!(" Back: {:.5}", back); - println!(" Error: {:.2}%\n", (3.14159 - back) / 3.14159 * 100.0); + println!(" Error: {:.2}%\n", (3.14159 - back as f64) / 3.14159 * 100.0); - // Test arithmetic let a = gf16_from_f32(1.5); let b = gf16_from_f32(2.5); let sum = gf16_add(a, b); @@ -39,17 +26,15 @@ fn main() { println!(" 2.5 - 1.5 = {:.2} (expected 1.0)", gf16_to_f32(diff)); println!(" 1.5 / 2.5 = {:.2} (expected 0.6)\n", gf16_to_f32(quot)); - // Test φ-quantization - let weight = 2.71828; + let weight: f32 = 2.71828; let quantized = gf16_phi_quantize(weight); let dequantized = gf16_phi_dequantize(quantized); println!("φ-Quantization:"); println!(" Original: {:.5}", weight); - println!(" Quantized: 0x{:04X}", quantized); + println!(" Quantized: 0x{:04X}", quantized.0); println!(" Dequantized: {:.5}\n", dequantized); - // Test predicates let zero = gf16_from_f32(0.0); let inf_val = gf16_from_f32(std::f32::INFINITY); let neg_val = gf16_from_f32(-5.0); @@ -60,7 +45,6 @@ fn main() { println!(" gf16_is_negative(neg): {}", gf16_is_negative(neg_val)); println!(" gf16_is_nan(GF16_NAN): {}\n", gf16_is_nan(GF16_NAN)); - // Test comparison let x = gf16_from_f32(1.0); let y = gf16_from_f32(2.0); let z = gf16_from_f32(1.0); @@ -71,20 +55,17 @@ fn main() { println!(" gf16_cmp(1.0, 2.0): {}", gf16_cmp(x, y)); println!(" gf16_cmp(2.0, 1.0): {}\n", gf16_cmp(y, x)); - // Test FMA (fused multiply-add) let four = gf16_from_f32(4.0); let fma_result = gf16_fma(a, b, four); println!("FMA:"); println!(" 1.5 * 2.5 + 4.0 = {:.2} (expected 7.75)\n", gf16_to_f32(fma_result)); - // Test bit extraction - println!("Bit Extraction (GF16_ONE = 0x{:04X}):", GF16_ONE); - println!(" GF16_SIGN(GF16_ONE): {}", GF16_SIGN(GF16_ONE)); - println!(" GF16_EXP(GF16_ONE): {}", GF16_EXP(GF16_ONE)); - println!(" GF16_MANT(GF16_ONE): {}\n", GF16_MANT(GF16_ONE)); + println!("Bit Extraction (GF16_ONE = 0x{:04X}):", GF16_ONE.0); + println!(" sign: {}", (GF16_ONE.0 >> 15) & 1); + println!(" exp: {}", (GF16_ONE.0 >> 9) & 0x3F); + println!(" mant: {}\n", GF16_ONE.0 & 0x1FF); - // Test library info let version = std::ffi::CStr::from_ptr(goldenfloat_version() as *const i8); let phi = goldenfloat_phi(); let trinity = goldenfloat_trinity(); From de783d478717ae25f50cb183499f27a18d878912 Mon Sep 17 00:00:00 2001 From: Dmitriy Vasilev Date: Thu, 30 Apr 2026 01:17:27 +0700 Subject: [PATCH 6/6] fix(build): shared step must depend on install step, not compile step The 'shared' named step depended on c_abi_lib.step (compile) instead of the install step returned by installArtifact(). This meant 'zig build shared' compiled the library but never copied it to zig-out/lib/, so all bindings failed to find it at link/runtime. --- build.zig | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/build.zig b/build.zig index 668a31b..85f867e 100644 --- a/build.zig +++ b/build.zig @@ -56,13 +56,12 @@ pub fn build(b: *std.Build) void { .version = .{ .major = 1, .minor = 1, .patch = 0 }, }); - b.installArtifact(c_abi_lib); + const c_abi_install = b.installArtifact(c_abi_lib); - // Install C header alongside library const header_install = b.addInstallHeaderFile(b.path("src/c/gf16.h"), "gf16.h"); const shared_step = b.step("shared", "Build C-ABI shared library (libgoldenfloat)"); - shared_step.dependOn(&c_abi_lib.step); + shared_step.dependOn(&c_abi_install.step); shared_step.dependOn(&header_install.step); // ───────────────────────────────────────────────────────────────── @@ -115,4 +114,37 @@ pub fn build(b: *std.Build) void { test_step.dependOn(&run_tests.step); test_step.dependOn(&run_transcendent_tests.step); test_step.dependOn(&run_c_abi_tests.step); + + // ───────────────────────────────────────────────────────────────── + // Benchmarks + // ───────────────────────────────────────────────────────────────── + const bench_008_module = b.createModule(.{ + .root_source_file = b.path("benches/bench_008_fashion_mnist.zig"), + .target = target, + .optimize = optimize, + }); + const bench_008 = b.addExecutable(.{ + .name = "bench_008_fashion_mnist", + .root_module = bench_008_module, + }); + const run_bench_008 = b.addRunArtifact(bench_008); + const bench_008_step = b.step("bench-008", "Run BENCH-008: Fashion-MNIST MLP Quantization Validation"); + bench_008_step.dependOn(&run_bench_008.step); + + const bench_009_module = b.createModule(.{ + .root_source_file = b.path("benches/bench_009_transformer_attention.zig"), + .target = target, + .optimize = optimize, + }); + const bench_009 = b.addExecutable(.{ + .name = "bench_009_transformer_attention", + .root_module = bench_009_module, + }); + const run_bench_009 = b.addRunArtifact(bench_009); + const bench_009_step = b.step("bench-009", "Run BENCH-009: Transformer Attention Pattern Analysis"); + bench_009_step.dependOn(&run_bench_009.step); + + const bench_step = b.step("bench", "Run all benchmarks"); + bench_step.dependOn(&run_bench_008.step); + bench_step.dependOn(&run_bench_009.step); }