usearch 2.21.4 → 2.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -24
- package/binding.gyp +15 -37
- package/c/CMakeLists.txt +4 -5
- package/c/README.md +7 -7
- package/c/lib.cpp +32 -14
- package/c/test.c +83 -1
- package/c/usearch.h +24 -3
- package/cpp/CMakeLists.txt +4 -0
- package/cpp/README.md +5 -5
- package/cpp/bench.cpp +97 -116
- package/cpp/test.cpp +117 -20
- package/include/usearch/index.hpp +224 -48
- package/include/usearch/index_dense.hpp +105 -40
- package/include/usearch/index_plugins.hpp +1387 -204
- package/javascript/dist/cjs/usearch.d.ts +8 -3
- package/javascript/dist/cjs/usearch.js +12 -4
- package/javascript/dist/esm/usearch.d.ts +8 -3
- package/javascript/dist/esm/usearch.js +12 -4
- package/javascript/lib.cpp +17 -0
- package/javascript/usearch.ts +14 -6
- package/numkong/.clang-tidy +30 -0
- package/{simsimd → numkong}/.cmake-format.py +18 -0
- package/numkong/.editorconfig +12 -0
- package/numkong/.git-blame-ignore-revs +123 -0
- package/numkong/.mailmap +15 -0
- package/numkong/.spi.yml +11 -0
- package/numkong/.swift-format +14 -0
- package/numkong/CITATION.cff +81 -0
- package/numkong/CONTRIBUTING.md +500 -0
- package/numkong/Cargo.toml +54 -0
- package/numkong/MANIFEST.in +8 -0
- package/numkong/Package.swift +84 -0
- package/numkong/README.md +569 -0
- package/numkong/bench/README.md +217 -0
- package/numkong/bench/bench-browser.html +323 -0
- package/numkong/bench/bench.cpp +344 -0
- package/numkong/bench/bench.hpp +816 -0
- package/numkong/bench/bench.js +148 -0
- package/numkong/bench/bench.mjs +616 -0
- package/numkong/bench/bench_attention.cpp +113 -0
- package/numkong/bench/bench_cast.cpp +264 -0
- package/numkong/bench/bench_cross_amx.cpp +62 -0
- package/numkong/bench/bench_cross_arm.cpp +245 -0
- package/numkong/bench/bench_cross_blas.cpp +309 -0
- package/numkong/bench/bench_cross_loongarch.cpp +106 -0
- package/numkong/bench/bench_cross_power.cpp +84 -0
- package/numkong/bench/bench_cross_rvv.cpp +121 -0
- package/numkong/bench/bench_cross_serial.cpp +151 -0
- package/numkong/bench/bench_cross_sme.cpp +163 -0
- package/numkong/bench/bench_cross_wasm.cpp +142 -0
- package/numkong/bench/bench_cross_x86.cpp +455 -0
- package/numkong/bench/bench_curved.cpp +210 -0
- package/numkong/bench/bench_dot.cpp +315 -0
- package/numkong/bench/bench_each.cpp +503 -0
- package/numkong/bench/bench_geospatial.cpp +112 -0
- package/numkong/bench/bench_maxsim.cpp +154 -0
- package/numkong/bench/bench_mesh.cpp +148 -0
- package/numkong/bench/bench_probability.cpp +60 -0
- package/numkong/bench/bench_reduce.cpp +371 -0
- package/numkong/bench/bench_set.cpp +73 -0
- package/numkong/bench/bench_sparse.cpp +198 -0
- package/numkong/bench/bench_spatial.cpp +347 -0
- package/numkong/bench/bench_trigonometry.cpp +137 -0
- package/numkong/bench/swift/Bench.swift +506 -0
- package/numkong/binding.gyp +169 -0
- package/numkong/build.rs +484 -0
- package/numkong/c/dispatch.h +512 -0
- package/numkong/c/dispatch_bf16.c +447 -0
- package/numkong/c/dispatch_bf16c.c +52 -0
- package/numkong/c/dispatch_e2m3.c +296 -0
- package/numkong/c/dispatch_e3m2.c +284 -0
- package/numkong/c/dispatch_e4m3.c +318 -0
- package/numkong/c/dispatch_e5m2.c +316 -0
- package/numkong/c/dispatch_f16.c +452 -0
- package/numkong/c/dispatch_f16c.c +58 -0
- package/numkong/c/dispatch_f32.c +434 -0
- package/numkong/c/dispatch_f32c.c +99 -0
- package/numkong/c/dispatch_f64.c +348 -0
- package/numkong/c/dispatch_f64c.c +98 -0
- package/numkong/c/dispatch_i16.c +96 -0
- package/numkong/c/dispatch_i32.c +89 -0
- package/numkong/c/dispatch_i4.c +153 -0
- package/numkong/c/dispatch_i64.c +86 -0
- package/numkong/c/dispatch_i8.c +348 -0
- package/numkong/c/dispatch_other.c +348 -0
- package/numkong/c/dispatch_u1.c +193 -0
- package/numkong/c/dispatch_u16.c +124 -0
- package/numkong/c/dispatch_u32.c +118 -0
- package/numkong/c/dispatch_u4.c +153 -0
- package/numkong/c/dispatch_u64.c +102 -0
- package/numkong/c/dispatch_u8.c +364 -0
- package/numkong/c/numkong.c +940 -0
- package/numkong/cmake/nk_arm_isa_probes.cmake +61 -0
- package/numkong/cmake/nk_isa_probe.cmake +90 -0
- package/numkong/cmake/nk_loongarch_isa_probes.cmake +15 -0
- package/numkong/cmake/nk_math_lib.cmake +46 -0
- package/numkong/cmake/nk_power_isa_probes.cmake +15 -0
- package/numkong/cmake/nk_riscv_isa_probes.cmake +19 -0
- package/numkong/cmake/nk_x86_isa_probes.cmake +35 -0
- package/numkong/cmake/toolchain-aarch64-gnu.cmake +86 -0
- package/numkong/cmake/toolchain-android-arm64.cmake +25 -0
- package/numkong/cmake/toolchain-android-armv7.cmake +25 -0
- package/numkong/cmake/toolchain-loongarch64-gnu.cmake +68 -0
- package/numkong/cmake/toolchain-ppc64le-gnu.cmake +74 -0
- package/numkong/cmake/toolchain-riscv64-gnu.cmake +107 -0
- package/numkong/cmake/toolchain-riscv64-llvm.cmake +114 -0
- package/numkong/cmake/toolchain-wasi.cmake +77 -0
- package/numkong/cmake/toolchain-wasm.cmake +70 -0
- package/numkong/cmake/toolchain-wasm64.cmake +62 -0
- package/numkong/cmake/toolchain-x86_64-llvm.cmake +47 -0
- package/numkong/include/README.md +643 -0
- package/numkong/include/module.modulemap +129 -0
- package/numkong/include/numkong/attention/sapphireamx.h +1363 -0
- package/numkong/include/numkong/attention/sme.h +2075 -0
- package/numkong/include/numkong/attention.h +49 -0
- package/numkong/include/numkong/capabilities.h +930 -0
- package/numkong/include/numkong/cast/README.md +262 -0
- package/numkong/include/numkong/cast/diamond.h +64 -0
- package/numkong/include/numkong/cast/haswell.h +823 -0
- package/numkong/include/numkong/cast/icelake.h +475 -0
- package/numkong/include/numkong/cast/loongsonasx.h +252 -0
- package/numkong/include/numkong/cast/neon.h +1174 -0
- package/numkong/include/numkong/cast/powervsx.h +449 -0
- package/numkong/include/numkong/cast/rvv.h +970 -0
- package/numkong/include/numkong/cast/sapphire.h +262 -0
- package/numkong/include/numkong/cast/serial.h +2336 -0
- package/numkong/include/numkong/cast/skylake.h +915 -0
- package/numkong/include/numkong/cast/v128relaxed.h +537 -0
- package/numkong/include/numkong/cast.h +254 -0
- package/numkong/include/numkong/cast.hpp +44 -0
- package/numkong/include/numkong/curved/README.md +223 -0
- package/numkong/include/numkong/curved/genoa.h +182 -0
- package/numkong/include/numkong/curved/haswell.h +276 -0
- package/numkong/include/numkong/curved/neon.h +329 -0
- package/numkong/include/numkong/curved/neonbfdot.h +211 -0
- package/numkong/include/numkong/curved/rvv.h +305 -0
- package/numkong/include/numkong/curved/serial.h +207 -0
- package/numkong/include/numkong/curved/skylake.h +457 -0
- package/numkong/include/numkong/curved/smef64.h +530 -0
- package/numkong/include/numkong/curved.h +513 -0
- package/numkong/include/numkong/curved.hpp +144 -0
- package/numkong/include/numkong/dot/README.md +442 -0
- package/numkong/include/numkong/dot/alder.h +563 -0
- package/numkong/include/numkong/dot/diamond.h +158 -0
- package/numkong/include/numkong/dot/genoa.h +289 -0
- package/numkong/include/numkong/dot/haswell.h +1723 -0
- package/numkong/include/numkong/dot/icelake.h +997 -0
- package/numkong/include/numkong/dot/loongsonasx.h +671 -0
- package/numkong/include/numkong/dot/neon.h +869 -0
- package/numkong/include/numkong/dot/neonbfdot.h +243 -0
- package/numkong/include/numkong/dot/neonfhm.h +358 -0
- package/numkong/include/numkong/dot/neonfp8.h +323 -0
- package/numkong/include/numkong/dot/neonsdot.h +622 -0
- package/numkong/include/numkong/dot/powervsx.h +752 -0
- package/numkong/include/numkong/dot/rvv.h +722 -0
- package/numkong/include/numkong/dot/rvvbb.h +72 -0
- package/numkong/include/numkong/dot/rvvbf16.h +123 -0
- package/numkong/include/numkong/dot/rvvhalf.h +129 -0
- package/numkong/include/numkong/dot/sapphire.h +141 -0
- package/numkong/include/numkong/dot/serial.h +874 -0
- package/numkong/include/numkong/dot/sierra.h +405 -0
- package/numkong/include/numkong/dot/skylake.h +1116 -0
- package/numkong/include/numkong/dot/sve.h +420 -0
- package/numkong/include/numkong/dot/svebfdot.h +75 -0
- package/numkong/include/numkong/dot/svehalf.h +168 -0
- package/numkong/include/numkong/dot/svesdot.h +90 -0
- package/numkong/include/numkong/dot/v128relaxed.h +1293 -0
- package/numkong/include/numkong/dot.h +1136 -0
- package/numkong/include/numkong/dot.hpp +94 -0
- package/numkong/include/numkong/dots/README.md +496 -0
- package/numkong/include/numkong/dots/alder.h +117 -0
- package/numkong/include/numkong/dots/diamond.h +86 -0
- package/numkong/include/numkong/dots/genoa.h +100 -0
- package/numkong/include/numkong/dots/graniteamx.h +733 -0
- package/numkong/include/numkong/dots/haswell.h +310 -0
- package/numkong/include/numkong/dots/icelake.h +180 -0
- package/numkong/include/numkong/dots/loongsonasx.h +176 -0
- package/numkong/include/numkong/dots/neon.h +123 -0
- package/numkong/include/numkong/dots/neonbfdot.h +59 -0
- package/numkong/include/numkong/dots/neonfhm.h +96 -0
- package/numkong/include/numkong/dots/neonfp8.h +99 -0
- package/numkong/include/numkong/dots/neonsdot.h +144 -0
- package/numkong/include/numkong/dots/powervsx.h +194 -0
- package/numkong/include/numkong/dots/rvv.h +2593 -0
- package/numkong/include/numkong/dots/sapphireamx.h +4017 -0
- package/numkong/include/numkong/dots/serial.h +2867 -0
- package/numkong/include/numkong/dots/sierra.h +100 -0
- package/numkong/include/numkong/dots/skylake.h +207 -0
- package/numkong/include/numkong/dots/sme.h +5041 -0
- package/numkong/include/numkong/dots/smebi32.h +477 -0
- package/numkong/include/numkong/dots/smef64.h +1328 -0
- package/numkong/include/numkong/dots/smehalf.h +47 -0
- package/numkong/include/numkong/dots/v128relaxed.h +317 -0
- package/numkong/include/numkong/dots.h +3110 -0
- package/numkong/include/numkong/dots.hpp +641 -0
- package/numkong/include/numkong/each/README.md +465 -0
- package/numkong/include/numkong/each/haswell.h +1658 -0
- package/numkong/include/numkong/each/icelake.h +272 -0
- package/numkong/include/numkong/each/neon.h +1138 -0
- package/numkong/include/numkong/each/neonbfdot.h +211 -0
- package/numkong/include/numkong/each/neonhalf.h +318 -0
- package/numkong/include/numkong/each/rvv.h +1121 -0
- package/numkong/include/numkong/each/sapphire.h +343 -0
- package/numkong/include/numkong/each/serial.h +282 -0
- package/numkong/include/numkong/each/skylake.h +1562 -0
- package/numkong/include/numkong/each/v128relaxed.h +562 -0
- package/numkong/include/numkong/each.h +2232 -0
- package/numkong/include/numkong/each.hpp +434 -0
- package/numkong/include/numkong/geospatial/README.md +147 -0
- package/numkong/include/numkong/geospatial/haswell.h +633 -0
- package/numkong/include/numkong/geospatial/neon.h +615 -0
- package/numkong/include/numkong/geospatial/rvv.h +701 -0
- package/numkong/include/numkong/geospatial/serial.h +309 -0
- package/numkong/include/numkong/geospatial/skylake.h +613 -0
- package/numkong/include/numkong/geospatial/v128relaxed.h +652 -0
- package/numkong/include/numkong/geospatial.h +453 -0
- package/numkong/include/numkong/geospatial.hpp +235 -0
- package/numkong/include/numkong/matrix.hpp +336 -0
- package/numkong/include/numkong/maxsim/README.md +187 -0
- package/numkong/include/numkong/maxsim/alder.h +511 -0
- package/numkong/include/numkong/maxsim/genoa.h +115 -0
- package/numkong/include/numkong/maxsim/haswell.h +553 -0
- package/numkong/include/numkong/maxsim/icelake.h +480 -0
- package/numkong/include/numkong/maxsim/neonsdot.h +394 -0
- package/numkong/include/numkong/maxsim/sapphireamx.h +877 -0
- package/numkong/include/numkong/maxsim/serial.h +490 -0
- package/numkong/include/numkong/maxsim/sme.h +993 -0
- package/numkong/include/numkong/maxsim/v128relaxed.h +280 -0
- package/numkong/include/numkong/maxsim.h +571 -0
- package/numkong/include/numkong/maxsim.hpp +133 -0
- package/numkong/include/numkong/mesh/README.md +219 -0
- package/numkong/include/numkong/mesh/haswell.h +2013 -0
- package/numkong/include/numkong/mesh/neon.h +1821 -0
- package/numkong/include/numkong/mesh/neonbfdot.h +771 -0
- package/numkong/include/numkong/mesh/rvv.h +936 -0
- package/numkong/include/numkong/mesh/serial.h +721 -0
- package/numkong/include/numkong/mesh/skylake.h +2344 -0
- package/numkong/include/numkong/mesh/v128relaxed.h +1081 -0
- package/numkong/include/numkong/mesh.h +666 -0
- package/numkong/include/numkong/mesh.hpp +701 -0
- package/numkong/include/numkong/numkong.h +78 -0
- package/numkong/include/numkong/numkong.hpp +58 -0
- package/numkong/include/numkong/probability/README.md +177 -0
- package/numkong/include/numkong/probability/haswell.h +267 -0
- package/numkong/include/numkong/probability/neon.h +255 -0
- package/numkong/include/numkong/probability/rvv.h +414 -0
- package/numkong/include/numkong/probability/serial.h +181 -0
- package/numkong/include/numkong/probability/skylake.h +324 -0
- package/numkong/include/numkong/probability.h +380 -0
- package/numkong/include/numkong/probability.hpp +120 -0
- package/numkong/include/numkong/random.h +50 -0
- package/numkong/include/numkong/random.hpp +285 -0
- package/numkong/include/numkong/reduce/README.md +552 -0
- package/numkong/include/numkong/reduce/alder.h +636 -0
- package/numkong/include/numkong/reduce/genoa.h +201 -0
- package/numkong/include/numkong/reduce/haswell.h +3785 -0
- package/numkong/include/numkong/reduce/icelake.h +549 -0
- package/numkong/include/numkong/reduce/neon.h +3980 -0
- package/numkong/include/numkong/reduce/neonbfdot.h +142 -0
- package/numkong/include/numkong/reduce/neonfhm.h +293 -0
- package/numkong/include/numkong/reduce/neonsdot.h +357 -0
- package/numkong/include/numkong/reduce/rvv.h +3533 -0
- package/numkong/include/numkong/reduce/serial.h +753 -0
- package/numkong/include/numkong/reduce/sierra.h +338 -0
- package/numkong/include/numkong/reduce/skylake.h +3794 -0
- package/numkong/include/numkong/reduce/sve.h +52 -0
- package/numkong/include/numkong/reduce/v128relaxed.h +2302 -0
- package/numkong/include/numkong/reduce.h +1582 -0
- package/numkong/include/numkong/reduce.hpp +778 -0
- package/numkong/include/numkong/scalar/README.md +89 -0
- package/numkong/include/numkong/scalar/haswell.h +122 -0
- package/numkong/include/numkong/scalar/loongsonasx.h +74 -0
- package/numkong/include/numkong/scalar/neon.h +122 -0
- package/numkong/include/numkong/scalar/neonhalf.h +70 -0
- package/numkong/include/numkong/scalar/powervsx.h +96 -0
- package/numkong/include/numkong/scalar/rvv.h +211 -0
- package/numkong/include/numkong/scalar/sapphire.h +74 -0
- package/numkong/include/numkong/scalar/serial.h +332 -0
- package/numkong/include/numkong/scalar/v128relaxed.h +56 -0
- package/numkong/include/numkong/scalar.h +696 -0
- package/numkong/include/numkong/set/README.md +179 -0
- package/numkong/include/numkong/set/haswell.h +334 -0
- package/numkong/include/numkong/set/icelake.h +485 -0
- package/numkong/include/numkong/set/loongsonasx.h +181 -0
- package/numkong/include/numkong/set/neon.h +363 -0
- package/numkong/include/numkong/set/powervsx.h +326 -0
- package/numkong/include/numkong/set/rvv.h +226 -0
- package/numkong/include/numkong/set/rvvbb.h +117 -0
- package/numkong/include/numkong/set/serial.h +174 -0
- package/numkong/include/numkong/set/sve.h +187 -0
- package/numkong/include/numkong/set/v128relaxed.h +240 -0
- package/numkong/include/numkong/set.h +471 -0
- package/numkong/include/numkong/set.hpp +114 -0
- package/numkong/include/numkong/sets/README.md +149 -0
- package/numkong/include/numkong/sets/haswell.h +63 -0
- package/numkong/include/numkong/sets/icelake.h +66 -0
- package/numkong/include/numkong/sets/loongsonasx.h +52 -0
- package/numkong/include/numkong/sets/neon.h +61 -0
- package/numkong/include/numkong/sets/powervsx.h +65 -0
- package/numkong/include/numkong/sets/serial.h +43 -0
- package/numkong/include/numkong/sets/smebi32.h +1135 -0
- package/numkong/include/numkong/sets/v128relaxed.h +58 -0
- package/numkong/include/numkong/sets.h +382 -0
- package/numkong/include/numkong/sparse/README.md +156 -0
- package/numkong/include/numkong/sparse/icelake.h +463 -0
- package/numkong/include/numkong/sparse/neon.h +288 -0
- package/numkong/include/numkong/sparse/serial.h +117 -0
- package/numkong/include/numkong/sparse/sve2.h +504 -0
- package/numkong/include/numkong/sparse/turin.h +322 -0
- package/numkong/include/numkong/sparse.h +363 -0
- package/numkong/include/numkong/sparse.hpp +113 -0
- package/numkong/include/numkong/spatial/README.md +439 -0
- package/numkong/include/numkong/spatial/alder.h +604 -0
- package/numkong/include/numkong/spatial/diamond.h +240 -0
- package/numkong/include/numkong/spatial/genoa.h +222 -0
- package/numkong/include/numkong/spatial/haswell.h +979 -0
- package/numkong/include/numkong/spatial/icelake.h +1067 -0
- package/numkong/include/numkong/spatial/loongsonasx.h +483 -0
- package/numkong/include/numkong/spatial/neon.h +846 -0
- package/numkong/include/numkong/spatial/neonbfdot.h +164 -0
- package/numkong/include/numkong/spatial/neonfp8.h +258 -0
- package/numkong/include/numkong/spatial/neonsdot.h +429 -0
- package/numkong/include/numkong/spatial/powervsx.h +738 -0
- package/numkong/include/numkong/spatial/rvv.h +991 -0
- package/numkong/include/numkong/spatial/rvvbf16.h +128 -0
- package/numkong/include/numkong/spatial/rvvhalf.h +120 -0
- package/numkong/include/numkong/spatial/serial.h +347 -0
- package/numkong/include/numkong/spatial/sierra.h +516 -0
- package/numkong/include/numkong/spatial/skylake.h +605 -0
- package/numkong/include/numkong/spatial/sve.h +246 -0
- package/numkong/include/numkong/spatial/svebfdot.h +125 -0
- package/numkong/include/numkong/spatial/svehalf.h +136 -0
- package/numkong/include/numkong/spatial/svesdot.h +143 -0
- package/numkong/include/numkong/spatial/v128relaxed.h +969 -0
- package/numkong/include/numkong/spatial.h +1681 -0
- package/numkong/include/numkong/spatial.hpp +183 -0
- package/numkong/include/numkong/spatials/README.md +580 -0
- package/numkong/include/numkong/spatials/alder.h +94 -0
- package/numkong/include/numkong/spatials/diamond.h +82 -0
- package/numkong/include/numkong/spatials/genoa.h +94 -0
- package/numkong/include/numkong/spatials/graniteamx.h +173 -0
- package/numkong/include/numkong/spatials/haswell.h +219 -0
- package/numkong/include/numkong/spatials/icelake.h +113 -0
- package/numkong/include/numkong/spatials/loongsonasx.h +153 -0
- package/numkong/include/numkong/spatials/neon.h +109 -0
- package/numkong/include/numkong/spatials/neonbfdot.h +60 -0
- package/numkong/include/numkong/spatials/neonfhm.h +92 -0
- package/numkong/include/numkong/spatials/neonfp8.h +111 -0
- package/numkong/include/numkong/spatials/neonsdot.h +143 -0
- package/numkong/include/numkong/spatials/powervsx.h +153 -0
- package/numkong/include/numkong/spatials/rvv.h +1976 -0
- package/numkong/include/numkong/spatials/sapphireamx.h +1149 -0
- package/numkong/include/numkong/spatials/serial.h +248 -0
- package/numkong/include/numkong/spatials/sierra.h +96 -0
- package/numkong/include/numkong/spatials/skylake.h +184 -0
- package/numkong/include/numkong/spatials/sme.h +1927 -0
- package/numkong/include/numkong/spatials/smef64.h +483 -0
- package/numkong/include/numkong/spatials/v128relaxed.h +240 -0
- package/numkong/include/numkong/spatials.h +3374 -0
- package/numkong/include/numkong/spatials.hpp +509 -0
- package/numkong/include/numkong/tensor.hpp +1981 -0
- package/numkong/include/numkong/trigonometry/README.md +184 -0
- package/numkong/include/numkong/trigonometry/haswell.h +653 -0
- package/numkong/include/numkong/trigonometry/neon.h +638 -0
- package/numkong/include/numkong/trigonometry/rvv.h +700 -0
- package/numkong/include/numkong/trigonometry/serial.h +703 -0
- package/numkong/include/numkong/trigonometry/skylake.h +725 -0
- package/numkong/include/numkong/trigonometry/v128relaxed.h +667 -0
- package/numkong/include/numkong/trigonometry.h +467 -0
- package/numkong/include/numkong/trigonometry.hpp +166 -0
- package/numkong/include/numkong/types.h +1695 -0
- package/numkong/include/numkong/types.hpp +6027 -0
- package/numkong/include/numkong/vector.hpp +766 -0
- package/numkong/javascript/@numkong-darwin-arm64/package.json +26 -0
- package/numkong/javascript/@numkong-darwin-x64/package.json +26 -0
- package/numkong/javascript/@numkong-linux-arm64/package.json +25 -0
- package/numkong/javascript/@numkong-linux-x64/package.json +25 -0
- package/numkong/javascript/@numkong-win32-arm64/package.json +25 -0
- package/numkong/javascript/@numkong-win32-x64/package.json +25 -0
- package/numkong/javascript/README.md +254 -0
- package/numkong/javascript/node-gyp-build.d.ts +4 -0
- package/numkong/javascript/numkong-browser.ts +40 -0
- package/numkong/javascript/numkong-wasm.ts +750 -0
- package/numkong/javascript/numkong.c +712 -0
- package/numkong/javascript/numkong.ts +613 -0
- package/{simsimd → numkong}/javascript/tsconfig-base.json +6 -3
- package/numkong/javascript/tsconfig-cjs.json +13 -0
- package/{simsimd → numkong}/javascript/tsconfig-esm.json +1 -1
- package/numkong/javascript/types.ts +784 -0
- package/numkong/numkong.gypi +104 -0
- package/numkong/package.json +108 -0
- package/numkong/probes/arm_neon.c +8 -0
- package/numkong/probes/arm_neon_bfdot.c +9 -0
- package/numkong/probes/arm_neon_fhm.c +9 -0
- package/numkong/probes/arm_neon_half.c +8 -0
- package/numkong/probes/arm_neon_sdot.c +9 -0
- package/numkong/probes/arm_neonfp8.c +9 -0
- package/numkong/probes/arm_sme.c +16 -0
- package/numkong/probes/arm_sme2.c +16 -0
- package/numkong/probes/arm_sme2p1.c +16 -0
- package/numkong/probes/arm_sme_bf16.c +16 -0
- package/numkong/probes/arm_sme_bi32.c +16 -0
- package/numkong/probes/arm_sme_f64.c +16 -0
- package/numkong/probes/arm_sme_fa64.c +14 -0
- package/numkong/probes/arm_sme_half.c +16 -0
- package/numkong/probes/arm_sme_lut2.c +15 -0
- package/numkong/probes/arm_sve.c +18 -0
- package/numkong/probes/arm_sve2.c +20 -0
- package/numkong/probes/arm_sve2p1.c +18 -0
- package/numkong/probes/arm_sve_bfdot.c +20 -0
- package/numkong/probes/arm_sve_half.c +18 -0
- package/numkong/probes/arm_sve_sdot.c +21 -0
- package/numkong/probes/loongarch_lasx.c +12 -0
- package/numkong/probes/power_vsx.c +12 -0
- package/numkong/probes/probe.js +127 -0
- package/numkong/probes/riscv_rvv.c +14 -0
- package/numkong/probes/riscv_rvv_bb.c +15 -0
- package/numkong/probes/riscv_rvv_bf16.c +17 -0
- package/numkong/probes/riscv_rvv_half.c +14 -0
- package/numkong/probes/wasm_v128relaxed.c +11 -0
- package/numkong/probes/x86_alder.c +17 -0
- package/numkong/probes/x86_diamond.c +17 -0
- package/numkong/probes/x86_genoa.c +17 -0
- package/numkong/probes/x86_graniteamx.c +19 -0
- package/numkong/probes/x86_haswell.c +11 -0
- package/numkong/probes/x86_icelake.c +17 -0
- package/numkong/probes/x86_sapphire.c +16 -0
- package/numkong/probes/x86_sapphireamx.c +18 -0
- package/numkong/probes/x86_sierra.c +17 -0
- package/numkong/probes/x86_skylake.c +15 -0
- package/numkong/probes/x86_turin.c +17 -0
- package/numkong/rust/README.md +698 -0
- package/numkong/rust/capabilities.rs +96 -0
- package/numkong/rust/cast.rs +314 -0
- package/numkong/rust/curved.rs +391 -0
- package/numkong/rust/each.rs +2721 -0
- package/numkong/rust/geospatial.rs +343 -0
- package/numkong/rust/matrix.rs +5424 -0
- package/numkong/rust/maxsim.rs +540 -0
- package/numkong/rust/mesh.rs +837 -0
- package/numkong/rust/numkong.rs +409 -0
- package/numkong/rust/probability.rs +272 -0
- package/numkong/rust/reduce.rs +1249 -0
- package/numkong/rust/set.rs +194 -0
- package/numkong/rust/sparse.rs +513 -0
- package/numkong/rust/spatial.rs +1815 -0
- package/numkong/rust/tensor.rs +7565 -0
- package/numkong/rust/types.rs +3666 -0
- package/numkong/rust/vector.rs +1899 -0
- package/numkong/rustfmt.toml +2 -0
- package/numkong/swift/Matrix.swift +1388 -0
- package/numkong/swift/MaxSim.swift +151 -0
- package/numkong/swift/NumKong.swift +283 -0
- package/numkong/swift/Numerics.swift +329 -0
- package/numkong/swift/README.md +433 -0
- package/numkong/swift/Scalar.swift +741 -0
- package/numkong/swift/Tensor.swift +209 -0
- package/numkong/test/README.md +315 -0
- package/numkong/test/playwright.config.ts +16 -0
- package/numkong/test/swift/Test.swift +534 -0
- package/numkong/test/test-browser-bundle-runner.mjs +36 -0
- package/numkong/test/test-browser-bundle.html +188 -0
- package/numkong/test/test-browser-runner.mjs +43 -0
- package/numkong/test/test-browser.html +305 -0
- package/numkong/test/test-dtypes.mjs +453 -0
- package/numkong/test/test-wasm.mjs +267 -0
- package/numkong/test/test.cpp +442 -0
- package/numkong/test/test.hpp +580 -0
- package/numkong/test/test.mjs +363 -0
- package/numkong/test/test_base.py +1041 -0
- package/numkong/test/test_cast.cpp +242 -0
- package/numkong/test/test_cross.hpp +551 -0
- package/numkong/test/test_cross.py +467 -0
- package/numkong/test/test_cross_amx.cpp +50 -0
- package/numkong/test/test_cross_arm.cpp +225 -0
- package/numkong/test/test_cross_blas.cpp +354 -0
- package/numkong/test/test_cross_loongarch.cpp +90 -0
- package/numkong/test/test_cross_power.cpp +73 -0
- package/numkong/test/test_cross_rvv.cpp +109 -0
- package/numkong/test/test_cross_serial.cpp +181 -0
- package/numkong/test/test_cross_sme.cpp +148 -0
- package/numkong/test/test_cross_wasm.cpp +141 -0
- package/numkong/test/test_cross_x86.cpp +442 -0
- package/numkong/test/test_curved.cpp +182 -0
- package/numkong/test/test_curved.py +207 -0
- package/numkong/test/test_dot.cpp +353 -0
- package/numkong/test/test_dot.py +296 -0
- package/numkong/test/test_each.cpp +437 -0
- package/numkong/test/test_each.py +756 -0
- package/numkong/test/test_geospatial.cpp +155 -0
- package/numkong/test/test_geospatial.py +241 -0
- package/numkong/test/test_maxsim.cpp +138 -0
- package/numkong/test/test_maxsim.py +209 -0
- package/numkong/test/test_mesh.cpp +203 -0
- package/numkong/test/test_mesh.py +394 -0
- package/numkong/test/test_probability.cpp +136 -0
- package/numkong/test/test_probability.py +166 -0
- package/numkong/test/test_reduce.cpp +563 -0
- package/numkong/test/test_reduce.py +781 -0
- package/numkong/test/test_set.cpp +151 -0
- package/numkong/test/test_set.py +120 -0
- package/numkong/test/test_similarities.py +579 -0
- package/numkong/test/test_sparse.cpp +121 -0
- package/numkong/test/test_sparse.py +118 -0
- package/numkong/test/test_spatial.cpp +518 -0
- package/numkong/test/test_spatial.py +269 -0
- package/numkong/test/test_tensor.cpp +884 -0
- package/numkong/test/test_tensor.py +1417 -0
- package/numkong/test/test_trigonometry.cpp +156 -0
- package/numkong/test/test_trigonometry.py +180 -0
- package/package.json +7 -8
- package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
- package/prebuilds/linux-arm64/usearch.node +0 -0
- package/prebuilds/linux-x64/usearch.node +0 -0
- package/fp16/CMakeLists.txt +0 -176
- package/fp16/LICENSE +0 -11
- package/fp16/README.md +0 -20
- package/fp16/bench/alt-element.cc +0 -89
- package/fp16/bench/from-alt-array.cc +0 -175
- package/fp16/bench/from-ieee-array.cc +0 -399
- package/fp16/bench/ieee-element.cc +0 -272
- package/fp16/bench/to-alt-array.cc +0 -91
- package/fp16/bench/to-ieee-array.cc +0 -303
- package/fp16/cmake/DownloadGoogleBenchmark.cmake +0 -15
- package/fp16/cmake/DownloadGoogleTest.cmake +0 -15
- package/fp16/cmake/DownloadPSimd.cmake +0 -15
- package/fp16/configure.py +0 -65
- package/fp16/confu.yaml +0 -8
- package/fp16/include/fp16/__init__.py +0 -0
- package/fp16/include/fp16/avx.py +0 -50
- package/fp16/include/fp16/avx2.py +0 -53
- package/fp16/include/fp16/bitcasts.h +0 -92
- package/fp16/include/fp16/fp16.h +0 -451
- package/fp16/include/fp16/psimd.h +0 -131
- package/fp16/include/fp16.h +0 -11
- package/fp16/jni/Android.mk +0 -6
- package/fp16/jni/Application.mk +0 -4
- package/fp16/test/alt-from-fp32-value.cc +0 -498
- package/fp16/test/alt-to-fp32-bits.cc +0 -263
- package/fp16/test/alt-to-fp32-psimd.cc +0 -145
- package/fp16/test/alt-to-fp32-value.cc +0 -348
- package/fp16/test/alt-to-fp32x2-psimd.cc +0 -245
- package/fp16/test/bitcasts.cc +0 -56
- package/fp16/test/ieee-from-fp32-value.cc +0 -523
- package/fp16/test/ieee-to-fp32-bits.cc +0 -333
- package/fp16/test/ieee-to-fp32-psimd.cc +0 -312
- package/fp16/test/ieee-to-fp32-value.cc +0 -420
- package/fp16/test/ieee-to-fp32x2-psimd.cc +0 -541
- package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +0 -96
- package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +0 -96
- package/fp16/test/peachpy/stubs.py +0 -38
- package/fp16/test/tables.cc +0 -530
- package/fp16/test/tables.h +0 -13
- package/fp16/third-party/THHalf.h +0 -92
- package/fp16/third-party/eigen-half.h +0 -170
- package/fp16/third-party/float16-compressor.h +0 -80
- package/fp16/third-party/half.hpp +0 -2916
- package/fp16/third-party/npy-halffloat.h +0 -328
- package/simsimd/.git-blame-ignore-revs +0 -2
- package/simsimd/CONTRIBUTING.md +0 -321
- package/simsimd/Cargo.toml +0 -49
- package/simsimd/MANIFEST.in +0 -4
- package/simsimd/Package.swift +0 -28
- package/simsimd/README.md +0 -1493
- package/simsimd/binding.gyp +0 -35
- package/simsimd/build.rs +0 -70
- package/simsimd/c/lib.c +0 -374
- package/simsimd/include/module.modulemap +0 -4
- package/simsimd/include/simsimd/binary.h +0 -486
- package/simsimd/include/simsimd/curved.h +0 -1541
- package/simsimd/include/simsimd/dot.h +0 -1853
- package/simsimd/include/simsimd/elementwise.h +0 -2465
- package/simsimd/include/simsimd/geospatial.h +0 -43
- package/simsimd/include/simsimd/mesh.h +0 -69
- package/simsimd/include/simsimd/probability.h +0 -606
- package/simsimd/include/simsimd/simsimd.h +0 -2567
- package/simsimd/include/simsimd/sparse.h +0 -1384
- package/simsimd/include/simsimd/spatial.h +0 -2335
- package/simsimd/include/simsimd/types.h +0 -666
- package/simsimd/javascript/fallback.ts +0 -233
- package/simsimd/javascript/lib.c +0 -125
- package/simsimd/javascript/node-gyp-build.d.ts +0 -1
- package/simsimd/javascript/simsimd.ts +0 -179
- package/simsimd/javascript/tsconfig-cjs.json +0 -8
- package/simsimd/package-lock.json +0 -1761
- package/simsimd/package.json +0 -61
- package/simsimd/rust/lib.rs +0 -1821
- package/simsimd/scripts/bench.cxx +0 -1170
- package/simsimd/scripts/bench.ipynb +0 -187
- package/simsimd/scripts/bench.js +0 -127
- package/simsimd/scripts/bench.rs +0 -876
- package/simsimd/scripts/bench_vectors.py +0 -734
- package/simsimd/scripts/bench_vectors_live.py +0 -213
- package/simsimd/scripts/test.c +0 -187
- package/simsimd/scripts/test.mjs +0 -220
- package/simsimd/scripts/test.py +0 -1505
- package/simsimd/swift/SimSIMD.swift +0 -149
- package/simsimd/swift/Test.swift +0 -128
- /package/{simsimd → numkong}/CODE_OF_CONDUCT.md +0 -0
- /package/{simsimd → numkong}/LICENSE +0 -0
- /package/{simsimd → numkong}/javascript/dist-package-cjs.json +0 -0
- /package/{simsimd → numkong}/javascript/dist-package-esm.json +0 -0
|
@@ -0,0 +1,3666 @@
|
|
|
1
|
+
//! Scalar types and conversion trait for mixed-precision computing.
|
|
2
|
+
//!
|
|
3
|
+
//! This module defines **15+ numeric types** spanning 1-bit packed storage to
|
|
4
|
+
//! 128-bit complex. All of them implement [`StorageElement`] (for containers) and
|
|
5
|
+
//! [`NumberLike`] (for conversion to / from `f32` / `f64`), so generic code can
|
|
6
|
+
//! treat them uniformly.
|
|
7
|
+
//!
|
|
8
|
+
//! ## IEEE / Brain Floats
|
|
9
|
+
//!
|
|
10
|
+
//! - [`struct@f16`]: IEEE 754 half-precision (16-bit) floating point
|
|
11
|
+
//! - [`bf16`]: Brain floating point (bfloat16) — truncated single precision
|
|
12
|
+
//! - The Rust primitives [`f32`](prim@f32) and [`f64`](prim@f64) also implement
|
|
13
|
+
//! these traits
|
|
14
|
+
//!
|
|
15
|
+
//! ## 8-bit Floats (OCP / MX)
|
|
16
|
+
//!
|
|
17
|
+
//! - [`e4m3`]: 4-exponent, 3-mantissa FP8 (max ≈ 448)
|
|
18
|
+
//! - [`e5m2`]: 5-exponent, 2-mantissa FP8 (wider range, ~57 344 max)
|
|
19
|
+
//! - [`e2m3`]: 2-exponent, 3-mantissa 6-bit float padded to 8-bit (max 7.5)
|
|
20
|
+
//! - [`e3m2`]: 3-exponent, 2-mantissa 6-bit float padded to 8-bit (max 28)
|
|
21
|
+
//!
|
|
22
|
+
//! ## Signed / Unsigned Integers
|
|
23
|
+
//!
|
|
24
|
+
//! `i8` / `u8`, `i16` / `u16`, `i32` / `u32`, `i64` / `u64` all implement
|
|
25
|
+
//! [`StorageElement`] and [`NumberLike`] via the primitive blanket impls below.
|
|
26
|
+
//!
|
|
27
|
+
//! ## Sub-byte Packed Types
|
|
28
|
+
//!
|
|
29
|
+
//! - [`i4x2`]: Packed pair of signed 4-bit integers (one byte → two values)
|
|
30
|
+
//! - [`u4x2`]: Packed pair of unsigned 4-bit integers
|
|
31
|
+
//! - [`u1x8`]: Packed 8 binary values in a single byte
|
|
32
|
+
//!
|
|
33
|
+
//! ## Complex
|
|
34
|
+
//!
|
|
35
|
+
//! - [`f16c`], [`bf16c`], [`f32c`], [`f64c`] — generic `complex<Scalar>` wrappers
|
|
36
|
+
//! that expose `re`, `im`, `conj`, and arithmetic operators.
|
|
37
|
+
//!
|
|
38
|
+
//! All types support standard arithmetic operations and conversion to / from
|
|
39
|
+
//! `f32` via the [`FloatLike`] trait.
|
|
40
|
+
|
|
41
|
+
#![allow(non_camel_case_types)]
|
|
42
|
+
|
|
43
|
+
#[link(name = "numkong")]
|
|
44
|
+
extern "C" {
|
|
45
|
+
fn nk_f32_to_f16(src: *const f32, dest: *mut u16);
|
|
46
|
+
fn nk_f16_to_f32(src: *const u16, dest: *mut f32);
|
|
47
|
+
fn nk_f32_to_bf16(src: *const f32, dest: *mut u16);
|
|
48
|
+
fn nk_bf16_to_f32(src: *const u16, dest: *mut f32);
|
|
49
|
+
fn nk_f32_to_e4m3(src: *const f32, dest: *mut u8);
|
|
50
|
+
fn nk_e4m3_to_f32(src: *const u8, dest: *mut f32);
|
|
51
|
+
fn nk_f32_to_e5m2(src: *const f32, dest: *mut u8);
|
|
52
|
+
fn nk_e5m2_to_f32(src: *const u8, dest: *mut f32);
|
|
53
|
+
fn nk_f32_to_e2m3(src: *const f32, dest: *mut u8);
|
|
54
|
+
fn nk_e2m3_to_f32(src: *const u8, dest: *mut f32);
|
|
55
|
+
fn nk_f32_to_e3m2(src: *const f32, dest: *mut u8);
|
|
56
|
+
fn nk_e3m2_to_f32(src: *const u8, dest: *mut f32);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/// Compatibility function for pre 1.85 Rust versions lacking `f32::abs`.
|
|
60
|
+
#[inline(always)]
|
|
61
|
+
pub(crate) fn f32_abs_compat(x: f32) -> f32 {
|
|
62
|
+
f32::from_bits(x.to_bits() & 0x7FFF_FFFF)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// Compatibility function for pre 1.85 Rust versions lacking `f32::round`.
|
|
66
|
+
#[inline(always)]
|
|
67
|
+
pub(crate) fn f32_round_compat(x: f32) -> f32 {
|
|
68
|
+
let truncated_value = x as i32 as f32;
|
|
69
|
+
let fractional_part = x - truncated_value;
|
|
70
|
+
if fractional_part >= 0.5 {
|
|
71
|
+
truncated_value + 1.0
|
|
72
|
+
} else if fractional_part <= -0.5 {
|
|
73
|
+
truncated_value - 1.0
|
|
74
|
+
} else {
|
|
75
|
+
truncated_value
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// Compatibility function for pre 1.85 Rust versions lacking `f64::round`.
|
|
80
|
+
#[inline(always)]
|
|
81
|
+
pub(crate) fn f64_round_compat(x: f64) -> f64 {
|
|
82
|
+
let truncated_value = x as i64 as f64;
|
|
83
|
+
let fractional_part = x - truncated_value;
|
|
84
|
+
if fractional_part >= 0.5 {
|
|
85
|
+
truncated_value + 1.0
|
|
86
|
+
} else if fractional_part <= -0.5 {
|
|
87
|
+
truncated_value - 1.0
|
|
88
|
+
} else {
|
|
89
|
+
truncated_value
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/// Check whether two values are within tolerance: `|a - b| <= atol + rtol * |b|`.
|
|
94
|
+
#[inline]
|
|
95
|
+
pub fn is_close(a: f64, b: f64, atol: f64, rtol: f64) -> bool {
|
|
96
|
+
let diff = if a > b { a - b } else { b - a };
|
|
97
|
+
diff <= atol + rtol * (if b >= 0.0 { b } else { -b })
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// region: f16 Type
|
|
101
|
+
|
|
102
|
+
/// Half-precision (16-bit) IEEE 754 floating-point number.
|
|
103
|
+
///
|
|
104
|
+
/// Layout: sign(1) + exponent(5) + mantissa(10), bias=15.
|
|
105
|
+
/// Range: ±65504, epsilon at 1.0 ≈ 9.77×10⁻⁴, subnormal min ≈ 5.96×10⁻⁸.
|
|
106
|
+
/// 30 722 of 63 488 finite values (48.4%) fall in [−1, +1].
|
|
107
|
+
/// All arithmetic via f32 upcast/downcast.
|
|
108
|
+
///
|
|
109
|
+
/// # Examples
|
|
110
|
+
///
|
|
111
|
+
/// ```
|
|
112
|
+
/// use numkong::f16;
|
|
113
|
+
///
|
|
114
|
+
/// // Create from f32
|
|
115
|
+
/// let half = f16::from_f32(3.14);
|
|
116
|
+
///
|
|
117
|
+
/// // Convert back to f32
|
|
118
|
+
/// let float = half.to_f32();
|
|
119
|
+
///
|
|
120
|
+
/// // Direct access to bits
|
|
121
|
+
/// let bits = half.0;
|
|
122
|
+
/// ```
|
|
123
|
+
#[repr(transparent)]
|
|
124
|
+
#[derive(Clone, Copy, PartialEq, Eq, Default)]
|
|
125
|
+
pub struct f16(pub u16);
|
|
126
|
+
|
|
127
|
+
impl f16 {
|
|
128
|
+
/// Positive zero.
|
|
129
|
+
pub const ZERO: Self = f16(0);
|
|
130
|
+
/// Positive one.
|
|
131
|
+
pub const ONE: Self = f16(0x3C00);
|
|
132
|
+
/// Negative one.
|
|
133
|
+
pub const NEG_ONE: Self = f16(0xBC00);
|
|
134
|
+
/// Quiet NaN (Not a Number).
|
|
135
|
+
pub const NAN: Self = f16(0x7E00);
|
|
136
|
+
|
|
137
|
+
/// Converts an `f32` value to `f16`. Out-of-range values saturate.
|
|
138
|
+
///
|
|
139
|
+
/// # Examples
|
|
140
|
+
///
|
|
141
|
+
/// ```
|
|
142
|
+
/// use numkong::f16;
|
|
143
|
+
/// // Roundtrip a value well inside f16's ±65504 range.
|
|
144
|
+
/// let half = f16::from_f32(3.14);
|
|
145
|
+
/// assert!((half.to_f32() - 3.14).abs() < 1e-2);
|
|
146
|
+
/// ```
|
|
147
|
+
#[inline(always)]
|
|
148
|
+
pub fn from_f32(value: f32) -> Self {
|
|
149
|
+
let mut result: u16 = 0;
|
|
150
|
+
unsafe { nk_f32_to_f16(&value, &mut result) };
|
|
151
|
+
f16(result)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/// Converts this value back to `f32`.
|
|
155
|
+
///
|
|
156
|
+
/// # Examples
|
|
157
|
+
///
|
|
158
|
+
/// ```
|
|
159
|
+
/// use numkong::f16;
|
|
160
|
+
/// let half = f16::from_f32(-2.5);
|
|
161
|
+
/// assert_eq!(half.to_f32(), -2.5);
|
|
162
|
+
/// ```
|
|
163
|
+
#[inline(always)]
|
|
164
|
+
pub fn to_f32(self) -> f32 {
|
|
165
|
+
let mut result: f32 = 0.0;
|
|
166
|
+
unsafe { nk_f16_to_f32(&self.0, &mut result) };
|
|
167
|
+
result
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/// Returns true if this value is NaN.
|
|
171
|
+
#[inline(always)]
|
|
172
|
+
pub fn is_nan(self) -> bool {
|
|
173
|
+
self.to_f32().is_nan()
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/// Returns true if this value is ±∞.
|
|
177
|
+
#[inline(always)]
|
|
178
|
+
pub fn is_infinite(self) -> bool {
|
|
179
|
+
self.to_f32().is_infinite()
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/// Returns true if this number is neither infinite nor NaN.
|
|
183
|
+
#[inline(always)]
|
|
184
|
+
pub fn is_finite(self) -> bool {
|
|
185
|
+
self.to_f32().is_finite()
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/// Returns |self|.
|
|
189
|
+
#[inline(always)]
|
|
190
|
+
pub fn abs(self) -> Self {
|
|
191
|
+
Self::from_f32(f32_abs_compat(self.to_f32()))
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/// Returns ⌊self⌋. Requires `std`.
|
|
195
|
+
#[cfg(feature = "std")]
|
|
196
|
+
#[inline(always)]
|
|
197
|
+
pub fn floor(self) -> Self {
|
|
198
|
+
Self::from_f32(self.to_f32().floor())
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/// Returns ⌈self⌉. Requires `std`.
|
|
202
|
+
#[cfg(feature = "std")]
|
|
203
|
+
#[inline(always)]
|
|
204
|
+
pub fn ceil(self) -> Self {
|
|
205
|
+
Self::from_f32(self.to_f32().ceil())
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/// Rounds to the nearest integer; half-way cases go away from zero. Requires `std`.
|
|
209
|
+
#[cfg(feature = "std")]
|
|
210
|
+
#[inline(always)]
|
|
211
|
+
pub fn round(self) -> Self {
|
|
212
|
+
Self::from_f32(self.to_f32().round())
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
impl core::fmt::Debug for f16 {
|
|
217
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
218
|
+
write!(f, "f16({}, 0x{:04x})", self.to_f32(), self.0)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
impl core::fmt::Display for f16 {
|
|
223
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
224
|
+
if f.alternate() {
|
|
225
|
+
core::fmt::Display::fmt(&self.to_f32(), f)?;
|
|
226
|
+
write!(f, " [0x{:04x}]", self.0)
|
|
227
|
+
} else {
|
|
228
|
+
core::fmt::Display::fmt(&self.to_f32(), f)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
impl core::fmt::LowerHex for f16 {
|
|
234
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
235
|
+
core::fmt::LowerHex::fmt(&self.0, f)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
impl core::fmt::UpperHex for f16 {
|
|
240
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
241
|
+
core::fmt::UpperHex::fmt(&self.0, f)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
impl core::fmt::Binary for f16 {
|
|
246
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
247
|
+
core::fmt::Binary::fmt(&self.0, f)
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
impl core::ops::Add for f16 {
|
|
252
|
+
type Output = Self;
|
|
253
|
+
#[inline(always)]
|
|
254
|
+
fn add(self, right: Self) -> Self::Output {
|
|
255
|
+
Self::from_f32(self.to_f32() + right.to_f32())
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
impl core::ops::Sub for f16 {
|
|
260
|
+
type Output = Self;
|
|
261
|
+
#[inline(always)]
|
|
262
|
+
fn sub(self, right: Self) -> Self::Output {
|
|
263
|
+
Self::from_f32(self.to_f32() - right.to_f32())
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
impl core::ops::Mul for f16 {
|
|
268
|
+
type Output = Self;
|
|
269
|
+
#[inline(always)]
|
|
270
|
+
fn mul(self, right: Self) -> Self::Output {
|
|
271
|
+
Self::from_f32(self.to_f32() * right.to_f32())
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
impl core::ops::Div for f16 {
|
|
276
|
+
type Output = Self;
|
|
277
|
+
#[inline(always)]
|
|
278
|
+
fn div(self, right: Self) -> Self::Output {
|
|
279
|
+
Self::from_f32(self.to_f32() / right.to_f32())
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
impl core::ops::Neg for f16 {
|
|
284
|
+
type Output = Self;
|
|
285
|
+
#[inline(always)]
|
|
286
|
+
fn neg(self) -> Self::Output {
|
|
287
|
+
Self::from_f32(-self.to_f32())
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
impl core::cmp::PartialOrd for f16 {
|
|
292
|
+
#[inline(always)]
|
|
293
|
+
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
|
294
|
+
self.to_f32().partial_cmp(&other.to_f32())
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// endregion: f16 Type
|
|
299
|
+
|
|
300
|
+
// region: bf16 Type
|
|
301
|
+
|
|
302
|
+
/// BFloat16 (16-bit) floating-point number — truncated IEEE 754 single-precision.
|
|
303
|
+
///
|
|
304
|
+
/// Layout: sign(1) + exponent(8) + mantissa(7), bias=127.
|
|
305
|
+
/// Range: ±3.39×10³⁸ (same dynamic range as f32), epsilon at 1.0 ≈ 7.81×10⁻³.
|
|
306
|
+
/// 32 514 of 65 280 finite values (49.8%) fall in [−1, +1].
|
|
307
|
+
/// Wider dynamic range than f16 but lower precision (7 vs 10 mantissa bits).
|
|
308
|
+
///
|
|
309
|
+
/// # Examples
|
|
310
|
+
///
|
|
311
|
+
/// ```
|
|
312
|
+
/// use numkong::bf16;
|
|
313
|
+
///
|
|
314
|
+
/// let brain = bf16::from_f32(3.14);
|
|
315
|
+
/// let float = brain.to_f32();
|
|
316
|
+
/// ```
|
|
317
|
+
#[repr(transparent)]
|
|
318
|
+
#[derive(Clone, Copy, PartialEq, Eq, Default)]
|
|
319
|
+
pub struct bf16(pub u16);
|
|
320
|
+
|
|
321
|
+
impl bf16 {
|
|
322
|
+
/// Positive zero.
|
|
323
|
+
pub const ZERO: Self = bf16(0);
|
|
324
|
+
|
|
325
|
+
/// Positive one.
|
|
326
|
+
pub const ONE: Self = bf16(0x3F80);
|
|
327
|
+
|
|
328
|
+
/// Negative one.
|
|
329
|
+
pub const NEG_ONE: Self = bf16(0xBF80);
|
|
330
|
+
/// Quiet NaN (Not a Number).
|
|
331
|
+
pub const NAN: Self = bf16(0x7FC0);
|
|
332
|
+
|
|
333
|
+
/// Converts an `f32` value to `bf16`. Out-of-range values saturate.
|
|
334
|
+
///
|
|
335
|
+
/// # Examples
|
|
336
|
+
///
|
|
337
|
+
/// ```
|
|
338
|
+
/// use numkong::bf16;
|
|
339
|
+
/// // bf16 matches f32's ±3.39e38 range but with only 7 bits of mantissa.
|
|
340
|
+
/// let brain = bf16::from_f32(1.0e30);
|
|
341
|
+
/// assert!((brain.to_f32() - 1.0e30).abs() < 1.0e28);
|
|
342
|
+
/// ```
|
|
343
|
+
#[inline(always)]
|
|
344
|
+
pub fn from_f32(value: f32) -> Self {
|
|
345
|
+
let mut result: u16 = 0;
|
|
346
|
+
unsafe { nk_f32_to_bf16(&value, &mut result) };
|
|
347
|
+
bf16(result)
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/// Converts this value back to `f32`.
|
|
351
|
+
#[inline(always)]
|
|
352
|
+
pub fn to_f32(self) -> f32 {
|
|
353
|
+
let mut result: f32 = 0.0;
|
|
354
|
+
unsafe { nk_bf16_to_f32(&self.0, &mut result) };
|
|
355
|
+
result
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/// Returns true if this value is NaN.
|
|
359
|
+
#[inline(always)]
|
|
360
|
+
pub fn is_nan(self) -> bool {
|
|
361
|
+
self.to_f32().is_nan()
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/// Returns true if this value is positive or negative infinity.
|
|
365
|
+
#[inline(always)]
|
|
366
|
+
pub fn is_infinite(self) -> bool {
|
|
367
|
+
self.to_f32().is_infinite()
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/// Returns true if this number is neither infinite nor NaN.
|
|
371
|
+
#[inline(always)]
|
|
372
|
+
pub fn is_finite(self) -> bool {
|
|
373
|
+
self.to_f32().is_finite()
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/// Returns the absolute value of self.
|
|
377
|
+
#[inline(always)]
|
|
378
|
+
pub fn abs(self) -> Self {
|
|
379
|
+
Self::from_f32(f32_abs_compat(self.to_f32()))
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/// Returns the largest integer less than or equal to a number.
|
|
383
|
+
///
|
|
384
|
+
/// This method is only available when the `std` feature is enabled.
|
|
385
|
+
#[cfg(feature = "std")]
|
|
386
|
+
#[inline(always)]
|
|
387
|
+
pub fn floor(self) -> Self {
|
|
388
|
+
Self::from_f32(self.to_f32().floor())
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/// Returns the smallest integer greater than or equal to a number.
|
|
392
|
+
///
|
|
393
|
+
/// This method is only available when the `std` feature is enabled.
|
|
394
|
+
#[cfg(feature = "std")]
|
|
395
|
+
#[inline(always)]
|
|
396
|
+
pub fn ceil(self) -> Self {
|
|
397
|
+
Self::from_f32(self.to_f32().ceil())
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/// Returns the nearest integer to a number. Round half-way cases away from 0.0.
|
|
401
|
+
///
|
|
402
|
+
/// This method is only available when the `std` feature is enabled.
|
|
403
|
+
#[cfg(feature = "std")]
|
|
404
|
+
#[inline(always)]
|
|
405
|
+
pub fn round(self) -> Self {
|
|
406
|
+
Self::from_f32(self.to_f32().round())
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
impl core::fmt::Debug for bf16 {
|
|
411
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
412
|
+
write!(f, "bf16({}, 0x{:04x})", self.to_f32(), self.0)
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
impl core::fmt::Display for bf16 {
|
|
417
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
418
|
+
if f.alternate() {
|
|
419
|
+
core::fmt::Display::fmt(&self.to_f32(), f)?;
|
|
420
|
+
write!(f, " [0x{:04x}]", self.0)
|
|
421
|
+
} else {
|
|
422
|
+
core::fmt::Display::fmt(&self.to_f32(), f)
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
impl core::fmt::LowerHex for bf16 {
|
|
428
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
429
|
+
core::fmt::LowerHex::fmt(&self.0, f)
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
impl core::fmt::UpperHex for bf16 {
|
|
434
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
435
|
+
core::fmt::UpperHex::fmt(&self.0, f)
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
impl core::fmt::Binary for bf16 {
|
|
440
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
441
|
+
core::fmt::Binary::fmt(&self.0, f)
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
impl core::ops::Add for bf16 {
|
|
446
|
+
type Output = Self;
|
|
447
|
+
#[inline(always)]
|
|
448
|
+
fn add(self, right: Self) -> Self::Output {
|
|
449
|
+
Self::from_f32(self.to_f32() + right.to_f32())
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
impl core::ops::Sub for bf16 {
|
|
454
|
+
type Output = Self;
|
|
455
|
+
#[inline(always)]
|
|
456
|
+
fn sub(self, right: Self) -> Self::Output {
|
|
457
|
+
Self::from_f32(self.to_f32() - right.to_f32())
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
impl core::ops::Mul for bf16 {
|
|
462
|
+
type Output = Self;
|
|
463
|
+
#[inline(always)]
|
|
464
|
+
fn mul(self, right: Self) -> Self::Output {
|
|
465
|
+
Self::from_f32(self.to_f32() * right.to_f32())
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
impl core::ops::Div for bf16 {
|
|
470
|
+
type Output = Self;
|
|
471
|
+
#[inline(always)]
|
|
472
|
+
fn div(self, right: Self) -> Self::Output {
|
|
473
|
+
Self::from_f32(self.to_f32() / right.to_f32())
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
impl core::ops::Neg for bf16 {
|
|
478
|
+
type Output = Self;
|
|
479
|
+
#[inline(always)]
|
|
480
|
+
fn neg(self) -> Self::Output {
|
|
481
|
+
Self::from_f32(-self.to_f32())
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
impl core::cmp::PartialOrd for bf16 {
|
|
486
|
+
#[inline(always)]
|
|
487
|
+
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
|
488
|
+
self.to_f32().partial_cmp(&other.to_f32())
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// endregion: bf16 Type
|
|
493
|
+
|
|
494
|
+
// region: e4m3 Type
|
|
495
|
+
|
|
496
|
+
/// 8-bit E4M3 floating-point number (OCP FP8).
|
|
497
|
+
///
|
|
498
|
+
/// Layout: sign(1) + exponent(4) + mantissa(3), bias=7.
|
|
499
|
+
/// Range: ±448, no infinities (all-ones exponent → NaN).
|
|
500
|
+
/// 114 of 254 finite values (44.9%) fall in [−1, +1].
|
|
501
|
+
/// Exact integer dot products via exponent-sum binning (29 bins).
|
|
502
|
+
///
|
|
503
|
+
/// # Examples
|
|
504
|
+
///
|
|
505
|
+
/// ```
|
|
506
|
+
/// use numkong::e4m3;
|
|
507
|
+
///
|
|
508
|
+
/// let fp8 = e4m3::from_f32(2.5);
|
|
509
|
+
/// let float = fp8.to_f32();
|
|
510
|
+
/// ```
|
|
511
|
+
#[repr(transparent)]
|
|
512
|
+
#[derive(Clone, Copy, PartialEq, Eq, Default)]
|
|
513
|
+
pub struct e4m3(pub u8);
|
|
514
|
+
|
|
515
|
+
impl e4m3 {
|
|
516
|
+
pub const ZERO: Self = e4m3(0x00);
|
|
517
|
+
pub const ONE: Self = e4m3(0x38);
|
|
518
|
+
pub const NEG_ONE: Self = e4m3(0xB8);
|
|
519
|
+
/// Quiet NaN (Not a Number).
|
|
520
|
+
pub const NAN: Self = e4m3(0x7F);
|
|
521
|
+
|
|
522
|
+
/// Converts an `f32` value to `e4m3`. Out-of-range values saturate.
|
|
523
|
+
///
|
|
524
|
+
/// # Examples
|
|
525
|
+
///
|
|
526
|
+
/// ```
|
|
527
|
+
/// use numkong::e4m3;
|
|
528
|
+
/// // e4m3 quantises to ~4 effective decimal digits inside ±448.
|
|
529
|
+
/// let fp8 = e4m3::from_f32(3.5);
|
|
530
|
+
/// assert_eq!(fp8.to_f32(), 3.5);
|
|
531
|
+
/// ```
|
|
532
|
+
#[inline(always)]
|
|
533
|
+
pub fn from_f32(value: f32) -> Self {
|
|
534
|
+
let mut result: u8 = 0;
|
|
535
|
+
unsafe { nk_f32_to_e4m3(&value, &mut result) };
|
|
536
|
+
e4m3(result)
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/// Converts this value back to `f32`.
|
|
540
|
+
#[inline(always)]
|
|
541
|
+
pub fn to_f32(self) -> f32 {
|
|
542
|
+
let mut result: f32 = 0.0;
|
|
543
|
+
unsafe { nk_e4m3_to_f32(&self.0, &mut result) };
|
|
544
|
+
result
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/// Returns true if this value is NaN.
|
|
548
|
+
#[inline(always)]
|
|
549
|
+
pub fn is_nan(self) -> bool {
|
|
550
|
+
(self.0 & 0x7F) == 0x7F
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/// Returns true if this value is ±∞. Always false for E4M3 (no infinities).
|
|
554
|
+
#[inline(always)]
|
|
555
|
+
pub fn is_infinite(self) -> bool {
|
|
556
|
+
false
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/// Returns true if this number is neither infinite nor NaN.
|
|
560
|
+
/// Note: E4M3 format has no infinities.
|
|
561
|
+
#[inline(always)]
|
|
562
|
+
pub fn is_finite(self) -> bool {
|
|
563
|
+
!self.is_nan()
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/// Returns the absolute value of self.
|
|
567
|
+
#[inline(always)]
|
|
568
|
+
pub fn abs(self) -> Self {
|
|
569
|
+
Self::from_f32(f32_abs_compat(self.to_f32()))
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/// Returns the largest integer less than or equal to a number.
|
|
573
|
+
///
|
|
574
|
+
/// This method is only available when the `std` feature is enabled.
|
|
575
|
+
#[cfg(feature = "std")]
|
|
576
|
+
#[inline(always)]
|
|
577
|
+
pub fn floor(self) -> Self {
|
|
578
|
+
Self::from_f32(self.to_f32().floor())
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/// Returns the smallest integer greater than or equal to a number.
|
|
582
|
+
///
|
|
583
|
+
/// This method is only available when the `std` feature is enabled.
|
|
584
|
+
#[cfg(feature = "std")]
|
|
585
|
+
#[inline(always)]
|
|
586
|
+
pub fn ceil(self) -> Self {
|
|
587
|
+
Self::from_f32(self.to_f32().ceil())
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/// Returns the nearest integer to a number. Round half-way cases away from 0.0.
|
|
591
|
+
///
|
|
592
|
+
/// This method is only available when the `std` feature is enabled.
|
|
593
|
+
#[cfg(feature = "std")]
|
|
594
|
+
#[inline(always)]
|
|
595
|
+
pub fn round(self) -> Self {
|
|
596
|
+
Self::from_f32(self.to_f32().round())
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
impl core::fmt::Debug for e4m3 {
|
|
601
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
602
|
+
write!(f, "e4m3({}, 0x{:02x})", self.to_f32(), self.0)
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
impl core::fmt::Display for e4m3 {
|
|
607
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
608
|
+
if f.alternate() {
|
|
609
|
+
core::fmt::Display::fmt(&self.to_f32(), f)?;
|
|
610
|
+
write!(f, " [0x{:02x}]", self.0)
|
|
611
|
+
} else {
|
|
612
|
+
core::fmt::Display::fmt(&self.to_f32(), f)
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
impl core::fmt::LowerHex for e4m3 {
|
|
618
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
619
|
+
core::fmt::LowerHex::fmt(&self.0, f)
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
impl core::fmt::UpperHex for e4m3 {
|
|
624
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
625
|
+
core::fmt::UpperHex::fmt(&self.0, f)
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
impl core::fmt::Binary for e4m3 {
|
|
630
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
631
|
+
core::fmt::Binary::fmt(&self.0, f)
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
impl core::ops::Add for e4m3 {
|
|
636
|
+
type Output = Self;
|
|
637
|
+
#[inline(always)]
|
|
638
|
+
fn add(self, right: Self) -> Self::Output {
|
|
639
|
+
Self::from_f32(self.to_f32() + right.to_f32())
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
impl core::ops::Sub for e4m3 {
|
|
644
|
+
type Output = Self;
|
|
645
|
+
#[inline(always)]
|
|
646
|
+
fn sub(self, right: Self) -> Self::Output {
|
|
647
|
+
Self::from_f32(self.to_f32() - right.to_f32())
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
impl core::ops::Mul for e4m3 {
|
|
652
|
+
type Output = Self;
|
|
653
|
+
#[inline(always)]
|
|
654
|
+
fn mul(self, right: Self) -> Self::Output {
|
|
655
|
+
Self::from_f32(self.to_f32() * right.to_f32())
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
impl core::ops::Div for e4m3 {
|
|
660
|
+
type Output = Self;
|
|
661
|
+
#[inline(always)]
|
|
662
|
+
fn div(self, right: Self) -> Self::Output {
|
|
663
|
+
Self::from_f32(self.to_f32() / right.to_f32())
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
impl core::ops::Neg for e4m3 {
|
|
668
|
+
type Output = Self;
|
|
669
|
+
#[inline(always)]
|
|
670
|
+
fn neg(self) -> Self::Output {
|
|
671
|
+
Self::from_f32(-self.to_f32())
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
impl core::cmp::PartialOrd for e4m3 {
|
|
676
|
+
#[inline(always)]
|
|
677
|
+
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
|
678
|
+
self.to_f32().partial_cmp(&other.to_f32())
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// endregion: e4m3 Type
|
|
683
|
+
|
|
684
|
+
// region: e5m2 Type
|
|
685
|
+
|
|
686
|
+
/// 8-bit E5M2 floating-point number (OCP FP8).
|
|
687
|
+
///
|
|
688
|
+
/// Layout: sign(1) + exponent(5) + mantissa(2), bias=15.
|
|
689
|
+
/// Range: ±57 344, supports infinities. Only 4 mantissa levels per exponent.
|
|
690
|
+
/// 122 of 248 finite values (49.2%) fall in [−1, +1].
|
|
691
|
+
/// High cancellation risk in dot products — consider compensated accumulation.
|
|
692
|
+
///
|
|
693
|
+
/// # Examples
|
|
694
|
+
///
|
|
695
|
+
/// ```
|
|
696
|
+
/// use numkong::e5m2;
|
|
697
|
+
///
|
|
698
|
+
/// let fp8 = e5m2::from_f32(2.5);
|
|
699
|
+
/// let float = fp8.to_f32();
|
|
700
|
+
/// ```
|
|
701
|
+
#[repr(transparent)]
|
|
702
|
+
#[derive(Clone, Copy, PartialEq, Eq, Default)]
|
|
703
|
+
pub struct e5m2(pub u8);
|
|
704
|
+
|
|
705
|
+
impl e5m2 {
|
|
706
|
+
/// Positive zero.
|
|
707
|
+
pub const ZERO: Self = e5m2(0x00);
|
|
708
|
+
|
|
709
|
+
/// Positive one.
|
|
710
|
+
pub const ONE: Self = e5m2(0x3C);
|
|
711
|
+
|
|
712
|
+
/// Negative one.
|
|
713
|
+
pub const NEG_ONE: Self = e5m2(0xBC);
|
|
714
|
+
/// Quiet NaN (Not a Number).
|
|
715
|
+
pub const NAN: Self = e5m2(0x7F);
|
|
716
|
+
|
|
717
|
+
/// Convert an `f32` value to `e5m2`. Out-of-range values saturate.
|
|
718
|
+
///
|
|
719
|
+
/// # Examples
|
|
720
|
+
///
|
|
721
|
+
/// ```
|
|
722
|
+
/// use numkong::e5m2;
|
|
723
|
+
/// // e5m2 trades mantissa precision for wider range (up to ~57 344).
|
|
724
|
+
/// let fp8 = e5m2::from_f32(1024.0);
|
|
725
|
+
/// assert_eq!(fp8.to_f32(), 1024.0);
|
|
726
|
+
/// ```
|
|
727
|
+
#[inline(always)]
|
|
728
|
+
pub fn from_f32(value: f32) -> Self {
|
|
729
|
+
let mut result: u8 = 0;
|
|
730
|
+
unsafe { nk_f32_to_e5m2(&value, &mut result) };
|
|
731
|
+
e5m2(result)
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
#[inline(always)]
|
|
735
|
+
pub fn to_f32(self) -> f32 {
|
|
736
|
+
let mut result: f32 = 0.0;
|
|
737
|
+
unsafe { nk_e5m2_to_f32(&self.0, &mut result) };
|
|
738
|
+
result
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/// Returns true if this value is NaN.
|
|
742
|
+
#[inline(always)]
|
|
743
|
+
pub fn is_nan(self) -> bool {
|
|
744
|
+
let exp = (self.0 >> 2) & 0x1F;
|
|
745
|
+
let mant = self.0 & 0x03;
|
|
746
|
+
exp == 0x1F && mant != 0
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/// Returns true if this value is positive or negative infinity.
|
|
750
|
+
#[inline(always)]
|
|
751
|
+
pub fn is_infinite(self) -> bool {
|
|
752
|
+
let exp = (self.0 >> 2) & 0x1F;
|
|
753
|
+
let mant = self.0 & 0x03;
|
|
754
|
+
exp == 0x1F && mant == 0
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/// Returns true if this number is neither infinite nor NaN.
|
|
758
|
+
#[inline(always)]
|
|
759
|
+
pub fn is_finite(self) -> bool {
|
|
760
|
+
let exp = (self.0 >> 2) & 0x1F;
|
|
761
|
+
exp != 0x1F
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/// Returns the absolute value of self.
|
|
765
|
+
#[inline(always)]
|
|
766
|
+
pub fn abs(self) -> Self {
|
|
767
|
+
Self::from_f32(f32_abs_compat(self.to_f32()))
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/// Returns the largest integer less than or equal to a number.
|
|
771
|
+
///
|
|
772
|
+
/// This method is only available when the `std` feature is enabled.
|
|
773
|
+
#[cfg(feature = "std")]
|
|
774
|
+
#[inline(always)]
|
|
775
|
+
pub fn floor(self) -> Self {
|
|
776
|
+
Self::from_f32(self.to_f32().floor())
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/// Returns the smallest integer greater than or equal to a number.
|
|
780
|
+
///
|
|
781
|
+
/// This method is only available when the `std` feature is enabled.
|
|
782
|
+
#[cfg(feature = "std")]
|
|
783
|
+
#[inline(always)]
|
|
784
|
+
pub fn ceil(self) -> Self {
|
|
785
|
+
Self::from_f32(self.to_f32().ceil())
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/// Returns the nearest integer to a number. Round half-way cases away from 0.0.
|
|
789
|
+
///
|
|
790
|
+
/// This method is only available when the `std` feature is enabled.
|
|
791
|
+
#[cfg(feature = "std")]
|
|
792
|
+
#[inline(always)]
|
|
793
|
+
pub fn round(self) -> Self {
|
|
794
|
+
Self::from_f32(self.to_f32().round())
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
impl core::fmt::Debug for e5m2 {
|
|
799
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
800
|
+
write!(f, "e5m2({}, 0x{:02x})", self.to_f32(), self.0)
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
impl core::fmt::Display for e5m2 {
|
|
805
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
806
|
+
if f.alternate() {
|
|
807
|
+
core::fmt::Display::fmt(&self.to_f32(), f)?;
|
|
808
|
+
write!(f, " [0x{:02x}]", self.0)
|
|
809
|
+
} else {
|
|
810
|
+
core::fmt::Display::fmt(&self.to_f32(), f)
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
impl core::fmt::LowerHex for e5m2 {
|
|
816
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
817
|
+
core::fmt::LowerHex::fmt(&self.0, f)
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
impl core::fmt::UpperHex for e5m2 {
|
|
822
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
823
|
+
core::fmt::UpperHex::fmt(&self.0, f)
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
impl core::fmt::Binary for e5m2 {
|
|
828
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
829
|
+
core::fmt::Binary::fmt(&self.0, f)
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
impl core::ops::Add for e5m2 {
|
|
834
|
+
type Output = Self;
|
|
835
|
+
#[inline(always)]
|
|
836
|
+
fn add(self, right: Self) -> Self::Output {
|
|
837
|
+
Self::from_f32(self.to_f32() + right.to_f32())
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
impl core::ops::Sub for e5m2 {
|
|
842
|
+
type Output = Self;
|
|
843
|
+
#[inline(always)]
|
|
844
|
+
fn sub(self, right: Self) -> Self::Output {
|
|
845
|
+
Self::from_f32(self.to_f32() - right.to_f32())
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
impl core::ops::Mul for e5m2 {
|
|
850
|
+
type Output = Self;
|
|
851
|
+
#[inline(always)]
|
|
852
|
+
fn mul(self, right: Self) -> Self::Output {
|
|
853
|
+
Self::from_f32(self.to_f32() * right.to_f32())
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
impl core::ops::Div for e5m2 {
|
|
858
|
+
type Output = Self;
|
|
859
|
+
#[inline(always)]
|
|
860
|
+
fn div(self, right: Self) -> Self::Output {
|
|
861
|
+
Self::from_f32(self.to_f32() / right.to_f32())
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
impl core::ops::Neg for e5m2 {
|
|
866
|
+
type Output = Self;
|
|
867
|
+
#[inline(always)]
|
|
868
|
+
fn neg(self) -> Self::Output {
|
|
869
|
+
Self::from_f32(-self.to_f32())
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
impl core::cmp::PartialOrd for e5m2 {
|
|
874
|
+
#[inline(always)]
|
|
875
|
+
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
|
876
|
+
self.to_f32().partial_cmp(&other.to_f32())
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// endregion: e5m2 Type
|
|
881
|
+
|
|
882
|
+
// region: e2m3 Type
|
|
883
|
+
|
|
884
|
+
/// 6-bit E2M3 micro-float (padded to 8-bit storage).
|
|
885
|
+
///
|
|
886
|
+
/// Layout: sign(1) + exponent(2) + mantissa(3), bias=1.
|
|
887
|
+
/// Range: ±7.5, no infinities. Only 64 total codes; 18 (28.1%) fall in [−1, +1].
|
|
888
|
+
/// 72% of codes lie outside [−1, +1] — poor resolution for normalized vectors.
|
|
889
|
+
/// Exact integer dot products via exponent-sum binning (15 bins).
|
|
890
|
+
///
|
|
891
|
+
/// # Examples
|
|
892
|
+
///
|
|
893
|
+
/// ```
|
|
894
|
+
/// use numkong::e2m3;
|
|
895
|
+
///
|
|
896
|
+
/// let fp6 = e2m3::from_f32(2.5);
|
|
897
|
+
/// let float = fp6.to_f32();
|
|
898
|
+
/// ```
|
|
899
|
+
#[repr(transparent)]
|
|
900
|
+
#[derive(Clone, Copy, PartialEq, Eq, Default)]
|
|
901
|
+
pub struct e2m3(pub u8);
|
|
902
|
+
|
|
903
|
+
impl e2m3 {
|
|
904
|
+
/// Positive zero.
|
|
905
|
+
pub const ZERO: Self = e2m3(0x00);
|
|
906
|
+
|
|
907
|
+
/// Positive one.
|
|
908
|
+
pub const ONE: Self = e2m3(0x08);
|
|
909
|
+
|
|
910
|
+
/// Negative one.
|
|
911
|
+
pub const NEG_ONE: Self = e2m3(0x28);
|
|
912
|
+
|
|
913
|
+
/// Convert an `f32` value to `e2m3`. Out-of-range values saturate.
|
|
914
|
+
///
|
|
915
|
+
/// # Examples
|
|
916
|
+
///
|
|
917
|
+
/// ```
|
|
918
|
+
/// use numkong::e2m3;
|
|
919
|
+
/// // e2m3 covers ±7.5 with 3 mantissa bits — suitable for fine ratios
|
|
920
|
+
/// // in a narrow dynamic range.
|
|
921
|
+
/// let tiny = e2m3::from_f32(1.5);
|
|
922
|
+
/// assert_eq!(tiny.to_f32(), 1.5);
|
|
923
|
+
/// ```
|
|
924
|
+
#[inline(always)]
|
|
925
|
+
pub fn from_f32(value: f32) -> Self {
|
|
926
|
+
let mut result: u8 = 0;
|
|
927
|
+
unsafe { nk_f32_to_e2m3(&value, &mut result) };
|
|
928
|
+
e2m3(result)
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
#[inline(always)]
|
|
932
|
+
pub fn to_f32(self) -> f32 {
|
|
933
|
+
let mut result: f32 = 0.0;
|
|
934
|
+
unsafe { nk_e2m3_to_f32(&self.0, &mut result) };
|
|
935
|
+
result
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/// Returns true if this value is NaN.
|
|
939
|
+
/// E2M3FN has no infinities - all special values are NaN.
|
|
940
|
+
#[inline(always)]
|
|
941
|
+
pub fn is_nan(self) -> bool {
|
|
942
|
+
false // E2M3FN has no NaN representation
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
/// Returns true if this value is positive or negative infinity.
|
|
946
|
+
/// E2M3FN format has no infinities.
|
|
947
|
+
#[inline(always)]
|
|
948
|
+
pub fn is_infinite(self) -> bool {
|
|
949
|
+
false
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/// Returns true if this number is neither infinite nor NaN.
|
|
953
|
+
/// Note: E2M3FN format has no infinities or NaN.
|
|
954
|
+
#[inline(always)]
|
|
955
|
+
pub fn is_finite(self) -> bool {
|
|
956
|
+
true
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
/// Returns the absolute value of self.
|
|
960
|
+
#[inline(always)]
|
|
961
|
+
pub fn abs(self) -> Self {
|
|
962
|
+
e2m3(self.0 & 0x1F) // Clear sign bit
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/// Returns the largest integer less than or equal to a number.
|
|
966
|
+
///
|
|
967
|
+
/// This method is only available when the `std` feature is enabled.
|
|
968
|
+
#[cfg(feature = "std")]
|
|
969
|
+
#[inline(always)]
|
|
970
|
+
pub fn floor(self) -> Self {
|
|
971
|
+
Self::from_f32(self.to_f32().floor())
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
/// Returns the smallest integer greater than or equal to a number.
|
|
975
|
+
///
|
|
976
|
+
/// This method is only available when the `std` feature is enabled.
|
|
977
|
+
#[cfg(feature = "std")]
|
|
978
|
+
#[inline(always)]
|
|
979
|
+
pub fn ceil(self) -> Self {
|
|
980
|
+
Self::from_f32(self.to_f32().ceil())
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
/// Returns the nearest integer to a number. Round half-way cases away from 0.0.
|
|
984
|
+
///
|
|
985
|
+
/// This method is only available when the `std` feature is enabled.
|
|
986
|
+
#[cfg(feature = "std")]
|
|
987
|
+
#[inline(always)]
|
|
988
|
+
pub fn round(self) -> Self {
|
|
989
|
+
Self::from_f32(self.to_f32().round())
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
impl core::fmt::Debug for e2m3 {
|
|
994
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
995
|
+
write!(f, "e2m3({}, 0x{:02x})", self.to_f32(), self.0)
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
impl core::fmt::Display for e2m3 {
|
|
1000
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1001
|
+
if f.alternate() {
|
|
1002
|
+
core::fmt::Display::fmt(&self.to_f32(), f)?;
|
|
1003
|
+
write!(f, " [0x{:02x}]", self.0)
|
|
1004
|
+
} else {
|
|
1005
|
+
core::fmt::Display::fmt(&self.to_f32(), f)
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
impl core::fmt::LowerHex for e2m3 {
|
|
1011
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1012
|
+
core::fmt::LowerHex::fmt(&self.0, f)
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
impl core::fmt::UpperHex for e2m3 {
|
|
1017
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1018
|
+
core::fmt::UpperHex::fmt(&self.0, f)
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
impl core::fmt::Binary for e2m3 {
|
|
1023
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1024
|
+
core::fmt::Binary::fmt(&self.0, f)
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
impl core::ops::Add for e2m3 {
|
|
1029
|
+
type Output = Self;
|
|
1030
|
+
#[inline(always)]
|
|
1031
|
+
fn add(self, right: Self) -> Self::Output {
|
|
1032
|
+
Self::from_f32(self.to_f32() + right.to_f32())
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
impl core::ops::Sub for e2m3 {
|
|
1037
|
+
type Output = Self;
|
|
1038
|
+
#[inline(always)]
|
|
1039
|
+
fn sub(self, right: Self) -> Self::Output {
|
|
1040
|
+
Self::from_f32(self.to_f32() - right.to_f32())
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
impl core::ops::Mul for e2m3 {
|
|
1045
|
+
type Output = Self;
|
|
1046
|
+
#[inline(always)]
|
|
1047
|
+
fn mul(self, right: Self) -> Self::Output {
|
|
1048
|
+
Self::from_f32(self.to_f32() * right.to_f32())
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
impl core::ops::Div for e2m3 {
|
|
1053
|
+
type Output = Self;
|
|
1054
|
+
#[inline(always)]
|
|
1055
|
+
fn div(self, right: Self) -> Self::Output {
|
|
1056
|
+
Self::from_f32(self.to_f32() / right.to_f32())
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
impl core::ops::Neg for e2m3 {
|
|
1061
|
+
type Output = Self;
|
|
1062
|
+
#[inline(always)]
|
|
1063
|
+
fn neg(self) -> Self::Output {
|
|
1064
|
+
Self::from_f32(-self.to_f32())
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
impl core::cmp::PartialOrd for e2m3 {
|
|
1069
|
+
#[inline(always)]
|
|
1070
|
+
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
|
1071
|
+
self.to_f32().partial_cmp(&other.to_f32())
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
// endregion: e2m3 Type
|
|
1076
|
+
|
|
1077
|
+
// region: e3m2 Type
|
|
1078
|
+
|
|
1079
|
+
/// 6-bit E3M2 micro-float (padded to 8-bit storage).
|
|
1080
|
+
///
|
|
1081
|
+
/// Layout: sign(1) + exponent(3) + mantissa(2), bias=3.
|
|
1082
|
+
/// Range: ±28, supports infinities. Only 64 total codes; 26 (40.6%) fall in [−1, +1].
|
|
1083
|
+
/// Exact integer dot products via exponent-sum binning (15 bins).
|
|
1084
|
+
///
|
|
1085
|
+
/// # Examples
|
|
1086
|
+
///
|
|
1087
|
+
/// ```
|
|
1088
|
+
/// use numkong::e3m2;
|
|
1089
|
+
///
|
|
1090
|
+
/// let fp6 = e3m2::from_f32(2.5);
|
|
1091
|
+
/// let float = fp6.to_f32();
|
|
1092
|
+
/// ```
|
|
1093
|
+
#[repr(transparent)]
|
|
1094
|
+
#[derive(Clone, Copy, PartialEq, Eq, Default)]
|
|
1095
|
+
pub struct e3m2(pub u8);
|
|
1096
|
+
|
|
1097
|
+
impl e3m2 {
|
|
1098
|
+
/// Positive zero.
|
|
1099
|
+
pub const ZERO: Self = e3m2(0x00);
|
|
1100
|
+
|
|
1101
|
+
/// Positive one.
|
|
1102
|
+
pub const ONE: Self = e3m2(0x0C);
|
|
1103
|
+
|
|
1104
|
+
/// Negative one.
|
|
1105
|
+
pub const NEG_ONE: Self = e3m2(0x2C);
|
|
1106
|
+
|
|
1107
|
+
/// Convert an `f32` value to `e3m2`. Out-of-range values saturate.
|
|
1108
|
+
///
|
|
1109
|
+
/// # Examples
|
|
1110
|
+
///
|
|
1111
|
+
/// ```
|
|
1112
|
+
/// use numkong::e3m2;
|
|
1113
|
+
/// // e3m2 trades mantissa bits for a larger range (±28).
|
|
1114
|
+
/// let tiny = e3m2::from_f32(16.0);
|
|
1115
|
+
/// assert_eq!(tiny.to_f32(), 16.0);
|
|
1116
|
+
/// ```
|
|
1117
|
+
#[inline(always)]
|
|
1118
|
+
pub fn from_f32(value: f32) -> Self {
|
|
1119
|
+
let mut result: u8 = 0;
|
|
1120
|
+
unsafe { nk_f32_to_e3m2(&value, &mut result) };
|
|
1121
|
+
e3m2(result)
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
#[inline(always)]
|
|
1125
|
+
pub fn to_f32(self) -> f32 {
|
|
1126
|
+
let mut result: f32 = 0.0;
|
|
1127
|
+
unsafe { nk_e3m2_to_f32(&self.0, &mut result) };
|
|
1128
|
+
result
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
/// Returns true if this value is NaN.
|
|
1132
|
+
#[inline(always)]
|
|
1133
|
+
pub fn is_nan(self) -> bool {
|
|
1134
|
+
let exp = (self.0 >> 2) & 0x07;
|
|
1135
|
+
let mant = self.0 & 0x03;
|
|
1136
|
+
exp == 0x07 && mant != 0
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
/// Returns true if this value is positive or negative infinity.
|
|
1140
|
+
#[inline(always)]
|
|
1141
|
+
pub fn is_infinite(self) -> bool {
|
|
1142
|
+
let exp = (self.0 >> 2) & 0x07;
|
|
1143
|
+
let mant = self.0 & 0x03;
|
|
1144
|
+
exp == 0x07 && mant == 0
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
/// Returns true if this number is neither infinite nor NaN.
|
|
1148
|
+
#[inline(always)]
|
|
1149
|
+
pub fn is_finite(self) -> bool {
|
|
1150
|
+
let exp = (self.0 >> 2) & 0x07;
|
|
1151
|
+
exp != 0x07
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
/// Returns the absolute value of self.
|
|
1155
|
+
#[inline(always)]
|
|
1156
|
+
pub fn abs(self) -> Self {
|
|
1157
|
+
e3m2(self.0 & 0x1F) // Clear sign bit
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
/// Returns the largest integer less than or equal to a number.
|
|
1161
|
+
///
|
|
1162
|
+
/// This method is only available when the `std` feature is enabled.
|
|
1163
|
+
#[cfg(feature = "std")]
|
|
1164
|
+
#[inline(always)]
|
|
1165
|
+
pub fn floor(self) -> Self {
|
|
1166
|
+
Self::from_f32(self.to_f32().floor())
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
/// Returns the smallest integer greater than or equal to a number.
|
|
1170
|
+
///
|
|
1171
|
+
/// This method is only available when the `std` feature is enabled.
|
|
1172
|
+
#[cfg(feature = "std")]
|
|
1173
|
+
#[inline(always)]
|
|
1174
|
+
pub fn ceil(self) -> Self {
|
|
1175
|
+
Self::from_f32(self.to_f32().ceil())
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
/// Returns the nearest integer to a number. Round half-way cases away from 0.0.
|
|
1179
|
+
///
|
|
1180
|
+
/// This method is only available when the `std` feature is enabled.
|
|
1181
|
+
#[cfg(feature = "std")]
|
|
1182
|
+
#[inline(always)]
|
|
1183
|
+
pub fn round(self) -> Self {
|
|
1184
|
+
Self::from_f32(self.to_f32().round())
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
impl core::fmt::Debug for e3m2 {
|
|
1189
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1190
|
+
write!(f, "e3m2({}, 0x{:02x})", self.to_f32(), self.0)
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
impl core::fmt::Display for e3m2 {
|
|
1195
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1196
|
+
if f.alternate() {
|
|
1197
|
+
core::fmt::Display::fmt(&self.to_f32(), f)?;
|
|
1198
|
+
write!(f, " [0x{:02x}]", self.0)
|
|
1199
|
+
} else {
|
|
1200
|
+
core::fmt::Display::fmt(&self.to_f32(), f)
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
impl core::fmt::LowerHex for e3m2 {
|
|
1206
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1207
|
+
core::fmt::LowerHex::fmt(&self.0, f)
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
impl core::fmt::UpperHex for e3m2 {
|
|
1212
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1213
|
+
core::fmt::UpperHex::fmt(&self.0, f)
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
impl core::fmt::Binary for e3m2 {
|
|
1218
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1219
|
+
core::fmt::Binary::fmt(&self.0, f)
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
impl core::ops::Add for e3m2 {
|
|
1224
|
+
type Output = Self;
|
|
1225
|
+
#[inline(always)]
|
|
1226
|
+
fn add(self, right: Self) -> Self::Output {
|
|
1227
|
+
Self::from_f32(self.to_f32() + right.to_f32())
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
impl core::ops::Sub for e3m2 {
|
|
1232
|
+
type Output = Self;
|
|
1233
|
+
#[inline(always)]
|
|
1234
|
+
fn sub(self, right: Self) -> Self::Output {
|
|
1235
|
+
Self::from_f32(self.to_f32() - right.to_f32())
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
impl core::ops::Mul for e3m2 {
|
|
1240
|
+
type Output = Self;
|
|
1241
|
+
#[inline(always)]
|
|
1242
|
+
fn mul(self, right: Self) -> Self::Output {
|
|
1243
|
+
Self::from_f32(self.to_f32() * right.to_f32())
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
impl core::ops::Div for e3m2 {
|
|
1248
|
+
type Output = Self;
|
|
1249
|
+
#[inline(always)]
|
|
1250
|
+
fn div(self, right: Self) -> Self::Output {
|
|
1251
|
+
Self::from_f32(self.to_f32() / right.to_f32())
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
impl core::ops::Neg for e3m2 {
|
|
1256
|
+
type Output = Self;
|
|
1257
|
+
#[inline(always)]
|
|
1258
|
+
fn neg(self) -> Self::Output {
|
|
1259
|
+
Self::from_f32(-self.to_f32())
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
impl core::cmp::PartialOrd for e3m2 {
|
|
1264
|
+
#[inline(always)]
|
|
1265
|
+
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
|
1266
|
+
self.to_f32().partial_cmp(&other.to_f32())
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
// endregion: e3m2 Type
|
|
1271
|
+
|
|
1272
|
+
// region: From<f32> Conversions
|
|
1273
|
+
|
|
1274
|
+
impl From<f32> for f16 {
|
|
1275
|
+
#[inline(always)]
|
|
1276
|
+
fn from(value: f32) -> Self {
|
|
1277
|
+
Self::from_f32(value)
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
impl From<f16> for f32 {
|
|
1281
|
+
#[inline(always)]
|
|
1282
|
+
fn from(value: f16) -> Self {
|
|
1283
|
+
value.to_f32()
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
impl From<f32> for bf16 {
|
|
1288
|
+
#[inline(always)]
|
|
1289
|
+
fn from(value: f32) -> Self {
|
|
1290
|
+
Self::from_f32(value)
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
impl From<bf16> for f32 {
|
|
1294
|
+
#[inline(always)]
|
|
1295
|
+
fn from(value: bf16) -> Self {
|
|
1296
|
+
value.to_f32()
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
impl From<f32> for e4m3 {
|
|
1301
|
+
#[inline(always)]
|
|
1302
|
+
fn from(value: f32) -> Self {
|
|
1303
|
+
Self::from_f32(value)
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
impl From<e4m3> for f32 {
|
|
1307
|
+
#[inline(always)]
|
|
1308
|
+
fn from(value: e4m3) -> Self {
|
|
1309
|
+
value.to_f32()
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
impl From<f32> for e5m2 {
|
|
1314
|
+
#[inline(always)]
|
|
1315
|
+
fn from(value: f32) -> Self {
|
|
1316
|
+
Self::from_f32(value)
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
impl From<e5m2> for f32 {
|
|
1320
|
+
#[inline(always)]
|
|
1321
|
+
fn from(value: e5m2) -> Self {
|
|
1322
|
+
value.to_f32()
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
impl From<f32> for e2m3 {
|
|
1327
|
+
#[inline(always)]
|
|
1328
|
+
fn from(value: f32) -> Self {
|
|
1329
|
+
Self::from_f32(value)
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
impl From<e2m3> for f32 {
|
|
1333
|
+
#[inline(always)]
|
|
1334
|
+
fn from(value: e2m3) -> Self {
|
|
1335
|
+
value.to_f32()
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
impl From<f32> for e3m2 {
|
|
1340
|
+
#[inline(always)]
|
|
1341
|
+
fn from(value: f32) -> Self {
|
|
1342
|
+
Self::from_f32(value)
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
impl From<e3m2> for f32 {
|
|
1346
|
+
#[inline(always)]
|
|
1347
|
+
fn from(value: e3m2) -> Self {
|
|
1348
|
+
value.to_f32()
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
// endregion: From<f32> Conversions
|
|
1353
|
+
|
|
1354
|
+
// region: u1x8 Type
|
|
1355
|
+
|
|
1356
|
+
/// Packed 8-bit bit-vector (8 booleans in one byte).
|
|
1357
|
+
///
|
|
1358
|
+
/// Layout: 8 bits packed into one byte, LSB = dimension 0.
|
|
1359
|
+
/// Used for Hamming distance and Jaccard similarity via popcount.
|
|
1360
|
+
#[repr(transparent)]
|
|
1361
|
+
#[derive(Clone, Copy, PartialEq, Eq, Default)]
|
|
1362
|
+
pub struct u1x8(pub u8);
|
|
1363
|
+
|
|
1364
|
+
impl core::fmt::Debug for u1x8 {
|
|
1365
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1366
|
+
write!(f, "u1x8(0b{:08b}, 0x{:02x})", self.0, self.0)
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
impl core::fmt::Display for u1x8 {
|
|
1371
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1372
|
+
if f.alternate() {
|
|
1373
|
+
write!(f, "0b{:08b} [0x{:02x}]", self.0, self.0)
|
|
1374
|
+
} else {
|
|
1375
|
+
write!(f, "0b{:08b}", self.0)
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
impl core::fmt::LowerHex for u1x8 {
|
|
1381
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1382
|
+
core::fmt::LowerHex::fmt(&self.0, f)
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
impl core::fmt::UpperHex for u1x8 {
|
|
1387
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1388
|
+
core::fmt::UpperHex::fmt(&self.0, f)
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
impl core::fmt::Binary for u1x8 {
|
|
1393
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1394
|
+
core::fmt::Binary::fmt(&self.0, f)
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
impl u1x8 {
|
|
1399
|
+
/// Create from raw packed bits.
|
|
1400
|
+
#[inline(always)]
|
|
1401
|
+
pub const fn new(bits: u8) -> Self {
|
|
1402
|
+
u1x8(bits)
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
/// Get the raw packed bits.
|
|
1406
|
+
#[inline(always)]
|
|
1407
|
+
pub const fn bits(self) -> u8 {
|
|
1408
|
+
self.0
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
/// Construct from 8 booleans (b0 = LSB, b7 = MSB).
|
|
1412
|
+
#[inline(always)]
|
|
1413
|
+
pub const fn from_bools(
|
|
1414
|
+
b0: bool,
|
|
1415
|
+
b1: bool,
|
|
1416
|
+
b2: bool,
|
|
1417
|
+
b3: bool,
|
|
1418
|
+
b4: bool,
|
|
1419
|
+
b5: bool,
|
|
1420
|
+
b6: bool,
|
|
1421
|
+
b7: bool,
|
|
1422
|
+
) -> Self {
|
|
1423
|
+
u1x8(
|
|
1424
|
+
(b0 as u8)
|
|
1425
|
+
| ((b1 as u8) << 1)
|
|
1426
|
+
| ((b2 as u8) << 2)
|
|
1427
|
+
| ((b3 as u8) << 3)
|
|
1428
|
+
| ((b4 as u8) << 4)
|
|
1429
|
+
| ((b5 as u8) << 5)
|
|
1430
|
+
| ((b6 as u8) << 6)
|
|
1431
|
+
| ((b7 as u8) << 7),
|
|
1432
|
+
)
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
/// Extract to 8 booleans (b0 = LSB, b7 = MSB).
|
|
1436
|
+
#[inline(always)]
|
|
1437
|
+
pub const fn to_bools(self) -> (bool, bool, bool, bool, bool, bool, bool, bool) {
|
|
1438
|
+
(
|
|
1439
|
+
(self.0 & 1) != 0,
|
|
1440
|
+
(self.0 & 2) != 0,
|
|
1441
|
+
(self.0 & 4) != 0,
|
|
1442
|
+
(self.0 & 8) != 0,
|
|
1443
|
+
(self.0 & 16) != 0,
|
|
1444
|
+
(self.0 & 32) != 0,
|
|
1445
|
+
(self.0 & 64) != 0,
|
|
1446
|
+
(self.0 & 128) != 0,
|
|
1447
|
+
)
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
impl From<(bool, bool, bool, bool, bool, bool, bool, bool)> for u1x8 {
|
|
1452
|
+
#[inline(always)]
|
|
1453
|
+
fn from(b: (bool, bool, bool, bool, bool, bool, bool, bool)) -> Self {
|
|
1454
|
+
u1x8::from_bools(b.0, b.1, b.2, b.3, b.4, b.5, b.6, b.7)
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
impl From<u1x8> for (bool, bool, bool, bool, bool, bool, bool, bool) {
|
|
1459
|
+
#[inline(always)]
|
|
1460
|
+
fn from(v: u1x8) -> Self {
|
|
1461
|
+
v.to_bools()
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
// endregion: u1x8 Type
|
|
1466
|
+
|
|
1467
|
+
// region: u4x2 Type
|
|
1468
|
+
|
|
1469
|
+
/// Packed 4-bit unsigned integer pair (2 × u4 in one byte).
|
|
1470
|
+
///
|
|
1471
|
+
/// Layout: low nibble = first element, high nibble = second element.
|
|
1472
|
+
/// Range per element: [0, 15]. Elements zero-extended to u8 for arithmetic.
|
|
1473
|
+
#[repr(transparent)]
|
|
1474
|
+
#[derive(Clone, Copy, PartialEq, Eq, Default)]
|
|
1475
|
+
pub struct u4x2(pub u8);
|
|
1476
|
+
|
|
1477
|
+
impl core::fmt::Debug for u4x2 {
|
|
1478
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1479
|
+
let (low, high) = self.to_u8s();
|
|
1480
|
+
write!(f, "u4x2({}, {}, 0x{:02x})", low, high, self.0)
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
impl core::fmt::Display for u4x2 {
|
|
1485
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1486
|
+
let (low, high) = self.to_u8s();
|
|
1487
|
+
if f.alternate() {
|
|
1488
|
+
write!(f, "({}, {}) [0x{:02x}]", low, high, self.0)
|
|
1489
|
+
} else {
|
|
1490
|
+
write!(f, "({}, {})", low, high)
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
impl core::fmt::LowerHex for u4x2 {
|
|
1496
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1497
|
+
core::fmt::LowerHex::fmt(&self.0, f)
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
impl core::fmt::UpperHex for u4x2 {
|
|
1502
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1503
|
+
core::fmt::UpperHex::fmt(&self.0, f)
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
impl core::fmt::Binary for u4x2 {
|
|
1508
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1509
|
+
core::fmt::Binary::fmt(&self.0, f)
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
impl u4x2 {
|
|
1514
|
+
/// Create from raw packed byte.
|
|
1515
|
+
#[inline(always)]
|
|
1516
|
+
pub const fn new(packed: u8) -> Self {
|
|
1517
|
+
u4x2(packed)
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
/// Get the raw packed byte.
|
|
1521
|
+
#[inline(always)]
|
|
1522
|
+
pub const fn packed(self) -> u8 {
|
|
1523
|
+
self.0
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
/// Construct from two u8 values with saturation to 0..15.
|
|
1527
|
+
#[inline(always)]
|
|
1528
|
+
pub const fn from_u8s(low: u8, high: u8) -> Self {
|
|
1529
|
+
let low_sat = if low > 15 { 15 } else { low };
|
|
1530
|
+
let high_sat = if high > 15 { 15 } else { high };
|
|
1531
|
+
u4x2(low_sat | (high_sat << 4))
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
/// Extract to two u8 values (0..15 each).
|
|
1535
|
+
#[inline(always)]
|
|
1536
|
+
pub const fn to_u8s(self) -> (u8, u8) {
|
|
1537
|
+
(self.0 & 0x0F, self.0 >> 4)
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
impl From<(u8, u8)> for u4x2 {
|
|
1542
|
+
#[inline(always)]
|
|
1543
|
+
fn from(v: (u8, u8)) -> Self {
|
|
1544
|
+
u4x2::from_u8s(v.0, v.1)
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
impl From<u4x2> for (u8, u8) {
|
|
1549
|
+
#[inline(always)]
|
|
1550
|
+
fn from(v: u4x2) -> Self {
|
|
1551
|
+
v.to_u8s()
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
// endregion: u4x2 Type
|
|
1556
|
+
|
|
1557
|
+
// region: i4x2 Type
|
|
1558
|
+
|
|
1559
|
+
/// Packed 4-bit signed integer pair (2 × i4 in one byte).
|
|
1560
|
+
///
|
|
1561
|
+
/// Layout: low nibble = first element, high nibble = second element (two's complement).
|
|
1562
|
+
/// Range per element: [−8, +7]. Elements sign-extended to i8 for arithmetic.
|
|
1563
|
+
#[repr(transparent)]
|
|
1564
|
+
#[derive(Clone, Copy, PartialEq, Eq, Default)]
|
|
1565
|
+
pub struct i4x2(pub u8);
|
|
1566
|
+
|
|
1567
|
+
impl core::fmt::Debug for i4x2 {
|
|
1568
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1569
|
+
let (low, high) = self.to_i8s();
|
|
1570
|
+
write!(f, "i4x2({}, {}, 0x{:02x})", low, high, self.0)
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
impl core::fmt::Display for i4x2 {
|
|
1575
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1576
|
+
let (low, high) = self.to_i8s();
|
|
1577
|
+
if f.alternate() {
|
|
1578
|
+
write!(f, "({}, {}) [0x{:02x}]", low, high, self.0)
|
|
1579
|
+
} else {
|
|
1580
|
+
write!(f, "({}, {})", low, high)
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
impl core::fmt::LowerHex for i4x2 {
|
|
1586
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1587
|
+
core::fmt::LowerHex::fmt(&self.0, f)
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
impl core::fmt::UpperHex for i4x2 {
|
|
1592
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1593
|
+
core::fmt::UpperHex::fmt(&self.0, f)
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
impl core::fmt::Binary for i4x2 {
|
|
1598
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1599
|
+
core::fmt::Binary::fmt(&self.0, f)
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
impl i4x2 {
|
|
1604
|
+
/// Create from raw packed byte.
|
|
1605
|
+
#[inline(always)]
|
|
1606
|
+
pub const fn new(packed: u8) -> Self {
|
|
1607
|
+
i4x2(packed)
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
/// Get the raw packed byte.
|
|
1611
|
+
#[inline(always)]
|
|
1612
|
+
pub const fn packed(self) -> u8 {
|
|
1613
|
+
self.0
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
/// Construct from two i8 values with saturation to -8..7.
|
|
1617
|
+
#[inline(always)]
|
|
1618
|
+
pub const fn from_i8s(low: i8, high: i8) -> Self {
|
|
1619
|
+
let low_sat = if low < -8 {
|
|
1620
|
+
-8
|
|
1621
|
+
} else if low > 7 {
|
|
1622
|
+
7
|
|
1623
|
+
} else {
|
|
1624
|
+
low
|
|
1625
|
+
};
|
|
1626
|
+
let high_sat = if high < -8 {
|
|
1627
|
+
-8
|
|
1628
|
+
} else if high > 7 {
|
|
1629
|
+
7
|
|
1630
|
+
} else {
|
|
1631
|
+
high
|
|
1632
|
+
};
|
|
1633
|
+
i4x2(((low_sat as u8) & 0x0F) | (((high_sat as u8) & 0x0F) << 4))
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
/// Extract to two i8 values (sign-extended from 4 bits).
|
|
1637
|
+
#[inline(always)]
|
|
1638
|
+
pub const fn to_i8s(self) -> (i8, i8) {
|
|
1639
|
+
let low = (self.0 & 0x0F) as i8;
|
|
1640
|
+
let high = ((self.0 >> 4) & 0x0F) as i8;
|
|
1641
|
+
// Sign extend from 4 bits: if bit 3 is set, fill upper bits with 1s
|
|
1642
|
+
let low = if low & 0x08 != 0 {
|
|
1643
|
+
low | (!0x0Fi8)
|
|
1644
|
+
} else {
|
|
1645
|
+
low
|
|
1646
|
+
};
|
|
1647
|
+
let high = if high & 0x08 != 0 {
|
|
1648
|
+
high | (!0x0Fi8)
|
|
1649
|
+
} else {
|
|
1650
|
+
high
|
|
1651
|
+
};
|
|
1652
|
+
(low, high)
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
impl From<(i8, i8)> for i4x2 {
|
|
1657
|
+
#[inline(always)]
|
|
1658
|
+
fn from(v: (i8, i8)) -> Self {
|
|
1659
|
+
i4x2::from_i8s(v.0, v.1)
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
impl From<i4x2> for (i8, i8) {
|
|
1664
|
+
#[inline(always)]
|
|
1665
|
+
fn from(v: i4x2) -> Self {
|
|
1666
|
+
v.to_i8s()
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
// endregion: i4x2 Type
|
|
1671
|
+
|
|
1672
|
+
// region: StorageElement + NumberLike + FloatLike Traits
|
|
1673
|
+
|
|
1674
|
+
/// Minimal trait for types that can be stored in vectors and tensors.
|
|
1675
|
+
///
|
|
1676
|
+
/// Provides identity elements (`zero`, `one`) and sub-byte packing metadata.
|
|
1677
|
+
/// Does not require numeric conversion — use [`NumberLike`] for that.
|
|
1678
|
+
pub trait StorageElement: Sized + Copy + Clone + Default {
|
|
1679
|
+
/// The additive identity.
|
|
1680
|
+
fn zero() -> Self;
|
|
1681
|
+
/// The multiplicative identity.
|
|
1682
|
+
fn one() -> Self;
|
|
1683
|
+
/// Number of logical dimensions packed into one storage value.
|
|
1684
|
+
/// Default: 1 for all normal types. Override for sub-byte packed types.
|
|
1685
|
+
fn dimensions_per_value() -> usize {
|
|
1686
|
+
1
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
/// Trait for types that support conversion to/from f32 with classification and constants.
|
|
1691
|
+
///
|
|
1692
|
+
/// Provides a unified interface for all numeric types used in NumKong,
|
|
1693
|
+
/// including half-precision floats, mini-floats, integers, and packed types.
|
|
1694
|
+
pub trait NumberLike: StorageElement {
|
|
1695
|
+
/// Convert from f32 to this type.
|
|
1696
|
+
fn from_f32(v: f32) -> Self;
|
|
1697
|
+
/// Convert from this type to f32.
|
|
1698
|
+
fn to_f32(self) -> f32;
|
|
1699
|
+
/// Convert from f64 to this type (default: via f32 roundtrip).
|
|
1700
|
+
fn from_f64(v: f64) -> Self {
|
|
1701
|
+
Self::from_f32(v as f32)
|
|
1702
|
+
}
|
|
1703
|
+
/// Convert from this type to f64 (default: via f32 roundtrip).
|
|
1704
|
+
fn to_f64(self) -> f64 {
|
|
1705
|
+
self.to_f32() as f64
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
fn abs(self) -> Self {
|
|
1709
|
+
Self::from_f32(f32_abs_compat(self.to_f32()))
|
|
1710
|
+
}
|
|
1711
|
+
fn is_nan(self) -> bool {
|
|
1712
|
+
self.to_f32().is_nan()
|
|
1713
|
+
}
|
|
1714
|
+
fn is_finite(self) -> bool {
|
|
1715
|
+
self.to_f32().is_finite()
|
|
1716
|
+
}
|
|
1717
|
+
fn is_infinite(self) -> bool {
|
|
1718
|
+
self.to_f32().is_infinite()
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
fn has_infinity() -> bool {
|
|
1722
|
+
false
|
|
1723
|
+
}
|
|
1724
|
+
fn has_nan() -> bool {
|
|
1725
|
+
false
|
|
1726
|
+
}
|
|
1727
|
+
fn has_subnormals() -> bool {
|
|
1728
|
+
false
|
|
1729
|
+
}
|
|
1730
|
+
fn max_value() -> f32 {
|
|
1731
|
+
f32::MAX
|
|
1732
|
+
}
|
|
1733
|
+
fn min_positive() -> f32 {
|
|
1734
|
+
f32::MIN_POSITIVE
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
/// Backward-compatible alias: any type implementing [`NumberLike`] also implements `FloatLike`.
|
|
1739
|
+
pub trait FloatLike: NumberLike {}
|
|
1740
|
+
impl<Scalar: NumberLike> FloatLike for Scalar {}
|
|
1741
|
+
|
|
1742
|
+
impl StorageElement for f32 {
|
|
1743
|
+
fn zero() -> Self {
|
|
1744
|
+
0.0
|
|
1745
|
+
}
|
|
1746
|
+
fn one() -> Self {
|
|
1747
|
+
1.0
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
impl NumberLike for f32 {
|
|
1752
|
+
fn from_f32(v: f32) -> Self {
|
|
1753
|
+
v
|
|
1754
|
+
}
|
|
1755
|
+
fn to_f32(self) -> f32 {
|
|
1756
|
+
self
|
|
1757
|
+
}
|
|
1758
|
+
fn from_f64(v: f64) -> Self {
|
|
1759
|
+
v as f32
|
|
1760
|
+
}
|
|
1761
|
+
fn to_f64(self) -> f64 {
|
|
1762
|
+
self as f64
|
|
1763
|
+
}
|
|
1764
|
+
fn abs(self) -> Self {
|
|
1765
|
+
f32_abs_compat(self)
|
|
1766
|
+
}
|
|
1767
|
+
fn is_nan(self) -> bool {
|
|
1768
|
+
f32::is_nan(self)
|
|
1769
|
+
}
|
|
1770
|
+
fn is_finite(self) -> bool {
|
|
1771
|
+
f32::is_finite(self)
|
|
1772
|
+
}
|
|
1773
|
+
fn is_infinite(self) -> bool {
|
|
1774
|
+
f32::is_infinite(self)
|
|
1775
|
+
}
|
|
1776
|
+
fn has_infinity() -> bool {
|
|
1777
|
+
true
|
|
1778
|
+
}
|
|
1779
|
+
fn has_nan() -> bool {
|
|
1780
|
+
true
|
|
1781
|
+
}
|
|
1782
|
+
fn has_subnormals() -> bool {
|
|
1783
|
+
true
|
|
1784
|
+
}
|
|
1785
|
+
fn max_value() -> f32 {
|
|
1786
|
+
f32::MAX
|
|
1787
|
+
}
|
|
1788
|
+
fn min_positive() -> f32 {
|
|
1789
|
+
f32::MIN_POSITIVE
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
impl StorageElement for f64 {
|
|
1794
|
+
fn zero() -> Self {
|
|
1795
|
+
0.0
|
|
1796
|
+
}
|
|
1797
|
+
fn one() -> Self {
|
|
1798
|
+
1.0
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
impl NumberLike for f64 {
|
|
1803
|
+
fn from_f32(v: f32) -> Self {
|
|
1804
|
+
v as f64
|
|
1805
|
+
}
|
|
1806
|
+
fn to_f32(self) -> f32 {
|
|
1807
|
+
self as f32
|
|
1808
|
+
}
|
|
1809
|
+
fn from_f64(v: f64) -> Self {
|
|
1810
|
+
v
|
|
1811
|
+
}
|
|
1812
|
+
fn to_f64(self) -> f64 {
|
|
1813
|
+
self
|
|
1814
|
+
}
|
|
1815
|
+
fn abs(self) -> Self {
|
|
1816
|
+
f64::from_bits(self.to_bits() & 0x7FFF_FFFF_FFFF_FFFF)
|
|
1817
|
+
}
|
|
1818
|
+
fn is_nan(self) -> bool {
|
|
1819
|
+
f64::is_nan(self)
|
|
1820
|
+
}
|
|
1821
|
+
fn is_finite(self) -> bool {
|
|
1822
|
+
f64::is_finite(self)
|
|
1823
|
+
}
|
|
1824
|
+
fn is_infinite(self) -> bool {
|
|
1825
|
+
f64::is_infinite(self)
|
|
1826
|
+
}
|
|
1827
|
+
fn has_infinity() -> bool {
|
|
1828
|
+
true
|
|
1829
|
+
}
|
|
1830
|
+
fn has_nan() -> bool {
|
|
1831
|
+
true
|
|
1832
|
+
}
|
|
1833
|
+
fn has_subnormals() -> bool {
|
|
1834
|
+
true
|
|
1835
|
+
}
|
|
1836
|
+
fn max_value() -> f32 {
|
|
1837
|
+
f32::MAX
|
|
1838
|
+
}
|
|
1839
|
+
fn min_positive() -> f32 {
|
|
1840
|
+
f32::MIN_POSITIVE
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
impl StorageElement for f16 {
|
|
1845
|
+
fn zero() -> Self {
|
|
1846
|
+
f16(0)
|
|
1847
|
+
}
|
|
1848
|
+
fn one() -> Self {
|
|
1849
|
+
f16::from_f32(1.0)
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
impl NumberLike for f16 {
|
|
1854
|
+
fn from_f32(v: f32) -> Self {
|
|
1855
|
+
f16::from_f32(v)
|
|
1856
|
+
}
|
|
1857
|
+
fn to_f32(self) -> f32 {
|
|
1858
|
+
self.to_f32()
|
|
1859
|
+
}
|
|
1860
|
+
fn abs(self) -> Self {
|
|
1861
|
+
self.abs()
|
|
1862
|
+
}
|
|
1863
|
+
fn is_nan(self) -> bool {
|
|
1864
|
+
self.is_nan()
|
|
1865
|
+
}
|
|
1866
|
+
fn is_finite(self) -> bool {
|
|
1867
|
+
self.is_finite()
|
|
1868
|
+
}
|
|
1869
|
+
fn is_infinite(self) -> bool {
|
|
1870
|
+
self.is_infinite()
|
|
1871
|
+
}
|
|
1872
|
+
fn has_infinity() -> bool {
|
|
1873
|
+
true
|
|
1874
|
+
}
|
|
1875
|
+
fn has_nan() -> bool {
|
|
1876
|
+
true
|
|
1877
|
+
}
|
|
1878
|
+
fn has_subnormals() -> bool {
|
|
1879
|
+
true
|
|
1880
|
+
}
|
|
1881
|
+
fn max_value() -> f32 {
|
|
1882
|
+
65504.0
|
|
1883
|
+
}
|
|
1884
|
+
fn min_positive() -> f32 {
|
|
1885
|
+
6.1e-5
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
impl StorageElement for bf16 {
|
|
1890
|
+
fn zero() -> Self {
|
|
1891
|
+
bf16(0)
|
|
1892
|
+
}
|
|
1893
|
+
fn one() -> Self {
|
|
1894
|
+
bf16::from_f32(1.0)
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
impl NumberLike for bf16 {
|
|
1899
|
+
fn from_f32(v: f32) -> Self {
|
|
1900
|
+
bf16::from_f32(v)
|
|
1901
|
+
}
|
|
1902
|
+
fn to_f32(self) -> f32 {
|
|
1903
|
+
self.to_f32()
|
|
1904
|
+
}
|
|
1905
|
+
fn abs(self) -> Self {
|
|
1906
|
+
self.abs()
|
|
1907
|
+
}
|
|
1908
|
+
fn is_nan(self) -> bool {
|
|
1909
|
+
self.is_nan()
|
|
1910
|
+
}
|
|
1911
|
+
fn is_finite(self) -> bool {
|
|
1912
|
+
self.is_finite()
|
|
1913
|
+
}
|
|
1914
|
+
fn is_infinite(self) -> bool {
|
|
1915
|
+
self.is_infinite()
|
|
1916
|
+
}
|
|
1917
|
+
fn has_infinity() -> bool {
|
|
1918
|
+
true
|
|
1919
|
+
}
|
|
1920
|
+
fn has_nan() -> bool {
|
|
1921
|
+
true
|
|
1922
|
+
}
|
|
1923
|
+
fn has_subnormals() -> bool {
|
|
1924
|
+
true
|
|
1925
|
+
}
|
|
1926
|
+
fn max_value() -> f32 {
|
|
1927
|
+
3.4e38
|
|
1928
|
+
}
|
|
1929
|
+
fn min_positive() -> f32 {
|
|
1930
|
+
1.2e-38
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
impl StorageElement for e4m3 {
|
|
1935
|
+
fn zero() -> Self {
|
|
1936
|
+
e4m3(0)
|
|
1937
|
+
}
|
|
1938
|
+
fn one() -> Self {
|
|
1939
|
+
e4m3::from_f32(1.0)
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
impl NumberLike for e4m3 {
|
|
1944
|
+
fn from_f32(v: f32) -> Self {
|
|
1945
|
+
e4m3::from_f32(v)
|
|
1946
|
+
}
|
|
1947
|
+
fn to_f32(self) -> f32 {
|
|
1948
|
+
self.to_f32()
|
|
1949
|
+
}
|
|
1950
|
+
fn abs(self) -> Self {
|
|
1951
|
+
self.abs()
|
|
1952
|
+
}
|
|
1953
|
+
fn is_nan(self) -> bool {
|
|
1954
|
+
self.is_nan()
|
|
1955
|
+
}
|
|
1956
|
+
fn is_finite(self) -> bool {
|
|
1957
|
+
self.is_finite()
|
|
1958
|
+
}
|
|
1959
|
+
fn is_infinite(self) -> bool {
|
|
1960
|
+
self.is_infinite()
|
|
1961
|
+
}
|
|
1962
|
+
fn has_nan() -> bool {
|
|
1963
|
+
true
|
|
1964
|
+
}
|
|
1965
|
+
fn has_subnormals() -> bool {
|
|
1966
|
+
true
|
|
1967
|
+
}
|
|
1968
|
+
fn max_value() -> f32 {
|
|
1969
|
+
448.0
|
|
1970
|
+
}
|
|
1971
|
+
fn min_positive() -> f32 {
|
|
1972
|
+
0.001953125
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
impl StorageElement for e5m2 {
|
|
1977
|
+
fn zero() -> Self {
|
|
1978
|
+
e5m2(0)
|
|
1979
|
+
}
|
|
1980
|
+
fn one() -> Self {
|
|
1981
|
+
e5m2::from_f32(1.0)
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
impl NumberLike for e5m2 {
|
|
1986
|
+
fn from_f32(v: f32) -> Self {
|
|
1987
|
+
e5m2::from_f32(v)
|
|
1988
|
+
}
|
|
1989
|
+
fn to_f32(self) -> f32 {
|
|
1990
|
+
self.to_f32()
|
|
1991
|
+
}
|
|
1992
|
+
fn abs(self) -> Self {
|
|
1993
|
+
self.abs()
|
|
1994
|
+
}
|
|
1995
|
+
fn is_nan(self) -> bool {
|
|
1996
|
+
self.is_nan()
|
|
1997
|
+
}
|
|
1998
|
+
fn is_finite(self) -> bool {
|
|
1999
|
+
self.is_finite()
|
|
2000
|
+
}
|
|
2001
|
+
fn is_infinite(self) -> bool {
|
|
2002
|
+
self.is_infinite()
|
|
2003
|
+
}
|
|
2004
|
+
fn has_infinity() -> bool {
|
|
2005
|
+
true
|
|
2006
|
+
}
|
|
2007
|
+
fn has_nan() -> bool {
|
|
2008
|
+
true
|
|
2009
|
+
}
|
|
2010
|
+
fn has_subnormals() -> bool {
|
|
2011
|
+
true
|
|
2012
|
+
}
|
|
2013
|
+
fn max_value() -> f32 {
|
|
2014
|
+
57344.0
|
|
2015
|
+
}
|
|
2016
|
+
fn min_positive() -> f32 {
|
|
2017
|
+
6.103_515_6e-5
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
impl StorageElement for e2m3 {
|
|
2022
|
+
fn zero() -> Self {
|
|
2023
|
+
e2m3(0)
|
|
2024
|
+
}
|
|
2025
|
+
fn one() -> Self {
|
|
2026
|
+
e2m3::from_f32(1.0)
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
impl NumberLike for e2m3 {
|
|
2031
|
+
fn from_f32(v: f32) -> Self {
|
|
2032
|
+
e2m3::from_f32(v)
|
|
2033
|
+
}
|
|
2034
|
+
fn to_f32(self) -> f32 {
|
|
2035
|
+
self.to_f32()
|
|
2036
|
+
}
|
|
2037
|
+
fn abs(self) -> Self {
|
|
2038
|
+
self.abs()
|
|
2039
|
+
}
|
|
2040
|
+
fn is_nan(self) -> bool {
|
|
2041
|
+
self.is_nan()
|
|
2042
|
+
}
|
|
2043
|
+
fn is_finite(self) -> bool {
|
|
2044
|
+
self.is_finite()
|
|
2045
|
+
}
|
|
2046
|
+
fn is_infinite(self) -> bool {
|
|
2047
|
+
self.is_infinite()
|
|
2048
|
+
}
|
|
2049
|
+
fn has_subnormals() -> bool {
|
|
2050
|
+
true
|
|
2051
|
+
}
|
|
2052
|
+
fn max_value() -> f32 {
|
|
2053
|
+
7.5
|
|
2054
|
+
}
|
|
2055
|
+
fn min_positive() -> f32 {
|
|
2056
|
+
0.0625
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
impl StorageElement for e3m2 {
|
|
2061
|
+
fn zero() -> Self {
|
|
2062
|
+
e3m2(0)
|
|
2063
|
+
}
|
|
2064
|
+
fn one() -> Self {
|
|
2065
|
+
e3m2::from_f32(1.0)
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
impl NumberLike for e3m2 {
|
|
2070
|
+
fn from_f32(v: f32) -> Self {
|
|
2071
|
+
e3m2::from_f32(v)
|
|
2072
|
+
}
|
|
2073
|
+
fn to_f32(self) -> f32 {
|
|
2074
|
+
self.to_f32()
|
|
2075
|
+
}
|
|
2076
|
+
fn abs(self) -> Self {
|
|
2077
|
+
self.abs()
|
|
2078
|
+
}
|
|
2079
|
+
fn is_nan(self) -> bool {
|
|
2080
|
+
self.is_nan()
|
|
2081
|
+
}
|
|
2082
|
+
fn is_finite(self) -> bool {
|
|
2083
|
+
self.is_finite()
|
|
2084
|
+
}
|
|
2085
|
+
fn is_infinite(self) -> bool {
|
|
2086
|
+
self.is_infinite()
|
|
2087
|
+
}
|
|
2088
|
+
fn has_subnormals() -> bool {
|
|
2089
|
+
true
|
|
2090
|
+
}
|
|
2091
|
+
fn max_value() -> f32 {
|
|
2092
|
+
28.0
|
|
2093
|
+
}
|
|
2094
|
+
fn min_positive() -> f32 {
|
|
2095
|
+
0.125
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
impl StorageElement for i8 {
|
|
2100
|
+
fn zero() -> Self {
|
|
2101
|
+
0
|
|
2102
|
+
}
|
|
2103
|
+
fn one() -> Self {
|
|
2104
|
+
1
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
impl NumberLike for i8 {
|
|
2109
|
+
fn from_f32(v: f32) -> Self {
|
|
2110
|
+
f32_round_compat(v) as i8
|
|
2111
|
+
}
|
|
2112
|
+
fn to_f32(self) -> f32 {
|
|
2113
|
+
self as f32
|
|
2114
|
+
}
|
|
2115
|
+
fn from_f64(v: f64) -> Self {
|
|
2116
|
+
f64_round_compat(v) as i8
|
|
2117
|
+
}
|
|
2118
|
+
fn to_f64(self) -> f64 {
|
|
2119
|
+
self as f64
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
impl StorageElement for u8 {
|
|
2124
|
+
fn zero() -> Self {
|
|
2125
|
+
0
|
|
2126
|
+
}
|
|
2127
|
+
fn one() -> Self {
|
|
2128
|
+
1
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
impl NumberLike for u8 {
|
|
2133
|
+
fn from_f32(v: f32) -> Self {
|
|
2134
|
+
let r = f32_round_compat(v);
|
|
2135
|
+
if r < 0.0 {
|
|
2136
|
+
0
|
|
2137
|
+
} else {
|
|
2138
|
+
r as u8
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
fn to_f32(self) -> f32 {
|
|
2142
|
+
self as f32
|
|
2143
|
+
}
|
|
2144
|
+
fn from_f64(v: f64) -> Self {
|
|
2145
|
+
let r = f64_round_compat(v);
|
|
2146
|
+
if r < 0.0 {
|
|
2147
|
+
0
|
|
2148
|
+
} else {
|
|
2149
|
+
r as u8
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
fn to_f64(self) -> f64 {
|
|
2153
|
+
self as f64
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
impl StorageElement for i32 {
|
|
2158
|
+
fn zero() -> Self {
|
|
2159
|
+
0
|
|
2160
|
+
}
|
|
2161
|
+
fn one() -> Self {
|
|
2162
|
+
1
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
impl NumberLike for i32 {
|
|
2167
|
+
fn from_f32(v: f32) -> Self {
|
|
2168
|
+
f32_round_compat(v) as i32
|
|
2169
|
+
}
|
|
2170
|
+
fn to_f32(self) -> f32 {
|
|
2171
|
+
self as f32
|
|
2172
|
+
}
|
|
2173
|
+
fn from_f64(v: f64) -> Self {
|
|
2174
|
+
f64_round_compat(v) as i32
|
|
2175
|
+
}
|
|
2176
|
+
fn to_f64(self) -> f64 {
|
|
2177
|
+
self as f64
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
impl StorageElement for u32 {
|
|
2182
|
+
fn zero() -> Self {
|
|
2183
|
+
0
|
|
2184
|
+
}
|
|
2185
|
+
fn one() -> Self {
|
|
2186
|
+
1
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
impl NumberLike for u32 {
|
|
2191
|
+
fn from_f32(v: f32) -> Self {
|
|
2192
|
+
let r = f32_round_compat(v);
|
|
2193
|
+
if r < 0.0 {
|
|
2194
|
+
0
|
|
2195
|
+
} else {
|
|
2196
|
+
r as u32
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
fn to_f32(self) -> f32 {
|
|
2200
|
+
self as f32
|
|
2201
|
+
}
|
|
2202
|
+
fn from_f64(v: f64) -> Self {
|
|
2203
|
+
let r = f64_round_compat(v);
|
|
2204
|
+
if r < 0.0 {
|
|
2205
|
+
0
|
|
2206
|
+
} else {
|
|
2207
|
+
r as u32
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
fn to_f64(self) -> f64 {
|
|
2211
|
+
self as f64
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
impl StorageElement for i16 {
|
|
2216
|
+
fn zero() -> Self {
|
|
2217
|
+
0
|
|
2218
|
+
}
|
|
2219
|
+
fn one() -> Self {
|
|
2220
|
+
1
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
impl NumberLike for i16 {
|
|
2225
|
+
fn from_f32(v: f32) -> Self {
|
|
2226
|
+
f32_round_compat(v) as i16
|
|
2227
|
+
}
|
|
2228
|
+
fn to_f32(self) -> f32 {
|
|
2229
|
+
self as f32
|
|
2230
|
+
}
|
|
2231
|
+
fn from_f64(v: f64) -> Self {
|
|
2232
|
+
f64_round_compat(v) as i16
|
|
2233
|
+
}
|
|
2234
|
+
fn to_f64(self) -> f64 {
|
|
2235
|
+
self as f64
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
|
|
2239
|
+
impl StorageElement for u16 {
|
|
2240
|
+
fn zero() -> Self {
|
|
2241
|
+
0
|
|
2242
|
+
}
|
|
2243
|
+
fn one() -> Self {
|
|
2244
|
+
1
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
impl NumberLike for u16 {
|
|
2249
|
+
fn from_f32(v: f32) -> Self {
|
|
2250
|
+
let r = f32_round_compat(v);
|
|
2251
|
+
if r < 0.0 {
|
|
2252
|
+
0
|
|
2253
|
+
} else {
|
|
2254
|
+
r as u16
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
fn to_f32(self) -> f32 {
|
|
2258
|
+
self as f32
|
|
2259
|
+
}
|
|
2260
|
+
fn from_f64(v: f64) -> Self {
|
|
2261
|
+
let r = f64_round_compat(v);
|
|
2262
|
+
if r < 0.0 {
|
|
2263
|
+
0
|
|
2264
|
+
} else {
|
|
2265
|
+
r as u16
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
fn to_f64(self) -> f64 {
|
|
2269
|
+
self as f64
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
impl StorageElement for i64 {
|
|
2274
|
+
fn zero() -> Self {
|
|
2275
|
+
0
|
|
2276
|
+
}
|
|
2277
|
+
fn one() -> Self {
|
|
2278
|
+
1
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
impl NumberLike for i64 {
|
|
2283
|
+
fn from_f32(v: f32) -> Self {
|
|
2284
|
+
f32_round_compat(v) as i64
|
|
2285
|
+
}
|
|
2286
|
+
fn to_f32(self) -> f32 {
|
|
2287
|
+
self as f32
|
|
2288
|
+
}
|
|
2289
|
+
fn from_f64(v: f64) -> Self {
|
|
2290
|
+
f64_round_compat(v) as i64
|
|
2291
|
+
}
|
|
2292
|
+
fn to_f64(self) -> f64 {
|
|
2293
|
+
self as f64
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
impl StorageElement for u64 {
|
|
2298
|
+
fn zero() -> Self {
|
|
2299
|
+
0
|
|
2300
|
+
}
|
|
2301
|
+
fn one() -> Self {
|
|
2302
|
+
1
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
impl NumberLike for u64 {
|
|
2307
|
+
fn from_f32(v: f32) -> Self {
|
|
2308
|
+
let r = f32_round_compat(v);
|
|
2309
|
+
if r < 0.0 {
|
|
2310
|
+
0
|
|
2311
|
+
} else {
|
|
2312
|
+
r as u64
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
fn to_f32(self) -> f32 {
|
|
2316
|
+
self as f32
|
|
2317
|
+
}
|
|
2318
|
+
fn from_f64(v: f64) -> Self {
|
|
2319
|
+
let r = f64_round_compat(v);
|
|
2320
|
+
if r < 0.0 {
|
|
2321
|
+
0
|
|
2322
|
+
} else {
|
|
2323
|
+
r as u64
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
fn to_f64(self) -> f64 {
|
|
2327
|
+
self as f64
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
impl StorageElement for usize {
|
|
2332
|
+
fn zero() -> Self {
|
|
2333
|
+
0
|
|
2334
|
+
}
|
|
2335
|
+
fn one() -> Self {
|
|
2336
|
+
1
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
impl StorageElement for i4x2 {
|
|
2341
|
+
fn zero() -> Self {
|
|
2342
|
+
i4x2::from((0i8, 0i8))
|
|
2343
|
+
}
|
|
2344
|
+
fn one() -> Self {
|
|
2345
|
+
i4x2::from((1i8, 1i8))
|
|
2346
|
+
}
|
|
2347
|
+
fn dimensions_per_value() -> usize {
|
|
2348
|
+
2
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
impl NumberLike for i4x2 {
|
|
2353
|
+
fn from_f32(v: f32) -> Self {
|
|
2354
|
+
let r = f32_round_compat(v) as i8;
|
|
2355
|
+
i4x2::from((r, r))
|
|
2356
|
+
}
|
|
2357
|
+
fn to_f32(self) -> f32 {
|
|
2358
|
+
let (a, _) = self.into();
|
|
2359
|
+
a as f32
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
|
|
2363
|
+
impl StorageElement for u4x2 {
|
|
2364
|
+
fn zero() -> Self {
|
|
2365
|
+
u4x2::from((0u8, 0u8))
|
|
2366
|
+
}
|
|
2367
|
+
fn one() -> Self {
|
|
2368
|
+
u4x2::from((1u8, 1u8))
|
|
2369
|
+
}
|
|
2370
|
+
fn dimensions_per_value() -> usize {
|
|
2371
|
+
2
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
impl NumberLike for u4x2 {
|
|
2376
|
+
fn from_f32(v: f32) -> Self {
|
|
2377
|
+
let r = f32_round_compat(v);
|
|
2378
|
+
let r = if r < 0.0 { 0u8 } else { r as u8 };
|
|
2379
|
+
u4x2::from((r, r))
|
|
2380
|
+
}
|
|
2381
|
+
fn to_f32(self) -> f32 {
|
|
2382
|
+
let (a, _) = self.into();
|
|
2383
|
+
a as f32
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
impl StorageElement for u1x8 {
|
|
2388
|
+
fn zero() -> Self {
|
|
2389
|
+
u1x8(0x00)
|
|
2390
|
+
}
|
|
2391
|
+
fn one() -> Self {
|
|
2392
|
+
u1x8(0xFF)
|
|
2393
|
+
}
|
|
2394
|
+
fn dimensions_per_value() -> usize {
|
|
2395
|
+
8
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
impl NumberLike for u1x8 {
|
|
2400
|
+
fn from_f32(v: f32) -> Self {
|
|
2401
|
+
if v > 0.0 {
|
|
2402
|
+
u1x8(0xFF)
|
|
2403
|
+
} else {
|
|
2404
|
+
u1x8(0x00)
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
fn to_f32(self) -> f32 {
|
|
2408
|
+
self.0.count_ones() as f32
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
// endregion: StorageElement + NumberLike + FloatLike Traits
|
|
2413
|
+
|
|
2414
|
+
// region: Complex Types
|
|
2415
|
+
|
|
2416
|
+
#[inline(always)]
|
|
2417
|
+
fn complex_mul_components<Scalar>(
|
|
2418
|
+
left_re: Scalar,
|
|
2419
|
+
left_im: Scalar,
|
|
2420
|
+
right_re: Scalar,
|
|
2421
|
+
right_im: Scalar,
|
|
2422
|
+
) -> (Scalar, Scalar)
|
|
2423
|
+
where
|
|
2424
|
+
Scalar: Copy
|
|
2425
|
+
+ core::ops::Add<Output = Scalar>
|
|
2426
|
+
+ core::ops::Sub<Output = Scalar>
|
|
2427
|
+
+ core::ops::Mul<Output = Scalar>,
|
|
2428
|
+
{
|
|
2429
|
+
(
|
|
2430
|
+
left_re * right_re - left_im * right_im,
|
|
2431
|
+
left_re * right_im + left_im * right_re,
|
|
2432
|
+
)
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2435
|
+
#[inline(always)]
|
|
2436
|
+
fn complex_div_components<Scalar>(
|
|
2437
|
+
left_re: Scalar,
|
|
2438
|
+
left_im: Scalar,
|
|
2439
|
+
right_re: Scalar,
|
|
2440
|
+
right_im: Scalar,
|
|
2441
|
+
) -> (Scalar, Scalar)
|
|
2442
|
+
where
|
|
2443
|
+
Scalar: Copy
|
|
2444
|
+
+ core::ops::Add<Output = Scalar>
|
|
2445
|
+
+ core::ops::Sub<Output = Scalar>
|
|
2446
|
+
+ core::ops::Mul<Output = Scalar>
|
|
2447
|
+
+ core::ops::Div<Output = Scalar>,
|
|
2448
|
+
{
|
|
2449
|
+
let denom = right_re * right_re + right_im * right_im;
|
|
2450
|
+
(
|
|
2451
|
+
(left_re * right_re + left_im * right_im) / denom,
|
|
2452
|
+
(left_im * right_re - left_re * right_im) / denom,
|
|
2453
|
+
)
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
#[doc(hidden)]
|
|
2457
|
+
pub trait ComplexComponent:
|
|
2458
|
+
NumberLike
|
|
2459
|
+
+ core::ops::Add<Output = Self>
|
|
2460
|
+
+ core::ops::Sub<Output = Self>
|
|
2461
|
+
+ core::ops::Mul<Output = Self>
|
|
2462
|
+
+ core::ops::Div<Output = Self>
|
|
2463
|
+
+ core::ops::Neg<Output = Self>
|
|
2464
|
+
{
|
|
2465
|
+
type Norm: Copy + core::ops::Add<Output = Self::Norm>;
|
|
2466
|
+
|
|
2467
|
+
fn from_norm(value: Self::Norm) -> Self;
|
|
2468
|
+
fn norm_component(self) -> Self::Norm;
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
impl ComplexComponent for f16 {
|
|
2472
|
+
type Norm = f32;
|
|
2473
|
+
|
|
2474
|
+
fn from_norm(value: Self::Norm) -> Self {
|
|
2475
|
+
Self::from_f32(value)
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
fn norm_component(self) -> Self::Norm {
|
|
2479
|
+
self.to_f32() * self.to_f32()
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
|
|
2483
|
+
impl ComplexComponent for bf16 {
|
|
2484
|
+
type Norm = f32;
|
|
2485
|
+
|
|
2486
|
+
fn from_norm(value: Self::Norm) -> Self {
|
|
2487
|
+
Self::from_f32(value)
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
fn norm_component(self) -> Self::Norm {
|
|
2491
|
+
self.to_f32() * self.to_f32()
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
|
|
2495
|
+
impl ComplexComponent for f32 {
|
|
2496
|
+
type Norm = f32;
|
|
2497
|
+
|
|
2498
|
+
fn from_norm(value: Self::Norm) -> Self {
|
|
2499
|
+
value
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
fn norm_component(self) -> Self::Norm {
|
|
2503
|
+
self * self
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
impl ComplexComponent for f64 {
|
|
2508
|
+
type Norm = f64;
|
|
2509
|
+
|
|
2510
|
+
fn from_norm(value: Self::Norm) -> Self {
|
|
2511
|
+
value
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
fn norm_component(self) -> Self::Norm {
|
|
2515
|
+
self * self
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
/// Complex number with adjacent real and imaginary components of type `Scalar`.
|
|
2520
|
+
///
|
|
2521
|
+
/// Layout: `{re: Scalar, im: Scalar}`. Supports conjugate, norm², and component access.
|
|
2522
|
+
/// Concrete aliases: [`f16c`], [`bf16c`], [`f32c`], [`f64c`].
|
|
2523
|
+
#[repr(C)]
|
|
2524
|
+
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
|
2525
|
+
pub struct complex<Scalar> {
|
|
2526
|
+
pub re: Scalar,
|
|
2527
|
+
pub im: Scalar,
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
impl<Scalar> complex<Scalar> {
|
|
2531
|
+
pub const fn from_real_imag(re: Scalar, im: Scalar) -> Self {
|
|
2532
|
+
Self { re, im }
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
pub const fn to_real_imag(self) -> (Scalar, Scalar)
|
|
2536
|
+
where
|
|
2537
|
+
Scalar: Copy,
|
|
2538
|
+
{
|
|
2539
|
+
(self.re, self.im)
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2543
|
+
impl<Scalar: ComplexComponent> complex<Scalar> {
|
|
2544
|
+
pub fn conj(self) -> Self {
|
|
2545
|
+
Self {
|
|
2546
|
+
re: self.re,
|
|
2547
|
+
im: -self.im,
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
|
|
2551
|
+
pub fn norm_sqr(self) -> Scalar::Norm {
|
|
2552
|
+
self.re.norm_component() + self.im.norm_component()
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2556
|
+
/// Half-precision (32-bit) complex number — two [`f16`] components. Kernel outputs widened to f32c.
|
|
2557
|
+
pub type f16c = complex<f16>;
|
|
2558
|
+
/// BFloat16 (32-bit) complex number — two [`bf16`] components. Kernel outputs widened to f32c.
|
|
2559
|
+
pub type bf16c = complex<bf16>;
|
|
2560
|
+
/// Single-precision (64-bit) complex number — two `f32` components.
|
|
2561
|
+
pub type f32c = complex<f32>;
|
|
2562
|
+
/// Double-precision (128-bit) complex number — two `f64` components.
|
|
2563
|
+
pub type f64c = complex<f64>;
|
|
2564
|
+
|
|
2565
|
+
impl<Scalar: ComplexComponent> From<f32> for complex<Scalar> {
|
|
2566
|
+
fn from(value: f32) -> Self {
|
|
2567
|
+
Self {
|
|
2568
|
+
re: Scalar::from_f32(value),
|
|
2569
|
+
im: Scalar::zero(),
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
impl<Scalar: ComplexComponent> core::ops::Add for complex<Scalar> {
|
|
2575
|
+
type Output = Self;
|
|
2576
|
+
fn add(self, right: Self) -> Self::Output {
|
|
2577
|
+
Self {
|
|
2578
|
+
re: self.re + right.re,
|
|
2579
|
+
im: self.im + right.im,
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
|
|
2584
|
+
impl<Scalar: ComplexComponent> core::ops::Sub for complex<Scalar> {
|
|
2585
|
+
type Output = Self;
|
|
2586
|
+
fn sub(self, right: Self) -> Self::Output {
|
|
2587
|
+
Self {
|
|
2588
|
+
re: self.re - right.re,
|
|
2589
|
+
im: self.im - right.im,
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
impl<Scalar: ComplexComponent> core::ops::Mul for complex<Scalar> {
|
|
2595
|
+
type Output = Self;
|
|
2596
|
+
fn mul(self, right: Self) -> Self::Output {
|
|
2597
|
+
let (re, im) = complex_mul_components(self.re, self.im, right.re, right.im);
|
|
2598
|
+
Self { re, im }
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
|
|
2602
|
+
impl<Scalar: ComplexComponent> core::ops::Div for complex<Scalar> {
|
|
2603
|
+
type Output = Self;
|
|
2604
|
+
fn div(self, right: Self) -> Self::Output {
|
|
2605
|
+
let (re, im) = complex_div_components(self.re, self.im, right.re, right.im);
|
|
2606
|
+
Self { re, im }
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
impl<Scalar: ComplexComponent> core::ops::Neg for complex<Scalar> {
|
|
2611
|
+
type Output = Self;
|
|
2612
|
+
fn neg(self) -> Self::Output {
|
|
2613
|
+
Self {
|
|
2614
|
+
re: -self.re,
|
|
2615
|
+
im: -self.im,
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
impl<Scalar: NumberLike> core::fmt::Display for complex<Scalar> {
|
|
2621
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
2622
|
+
let re = self.re.to_f32();
|
|
2623
|
+
let im = self.im.to_f32();
|
|
2624
|
+
if let Some(prec) = f.precision() {
|
|
2625
|
+
if im < 0.0 {
|
|
2626
|
+
write!(f, "{:.prec$} - {:.prec$}i", re, -im)
|
|
2627
|
+
} else {
|
|
2628
|
+
write!(f, "{:.prec$} + {:.prec$}i", re, im)
|
|
2629
|
+
}
|
|
2630
|
+
} else if im < 0.0 {
|
|
2631
|
+
write!(f, "{} - {}i", re, -im)
|
|
2632
|
+
} else {
|
|
2633
|
+
write!(f, "{} + {}i", re, im)
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
|
|
2638
|
+
impl<Scalar: ComplexComponent> StorageElement for complex<Scalar> {
|
|
2639
|
+
fn zero() -> Self {
|
|
2640
|
+
Self {
|
|
2641
|
+
re: Scalar::zero(),
|
|
2642
|
+
im: Scalar::zero(),
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
fn one() -> Self {
|
|
2646
|
+
Self {
|
|
2647
|
+
re: Scalar::one(),
|
|
2648
|
+
im: Scalar::zero(),
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2652
|
+
|
|
2653
|
+
impl<Scalar: ComplexComponent> NumberLike for complex<Scalar> {
|
|
2654
|
+
fn from_f32(v: f32) -> Self {
|
|
2655
|
+
Self::from(v)
|
|
2656
|
+
}
|
|
2657
|
+
fn to_f32(self) -> f32 {
|
|
2658
|
+
self.re.to_f32()
|
|
2659
|
+
}
|
|
2660
|
+
fn from_f64(v: f64) -> Self {
|
|
2661
|
+
Self {
|
|
2662
|
+
re: Scalar::from_f64(v),
|
|
2663
|
+
im: Scalar::zero(),
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
fn to_f64(self) -> f64 {
|
|
2667
|
+
self.re.to_f64()
|
|
2668
|
+
}
|
|
2669
|
+
fn abs(self) -> Self {
|
|
2670
|
+
Self {
|
|
2671
|
+
re: Scalar::from_norm(self.norm_sqr()),
|
|
2672
|
+
im: Scalar::zero(),
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
fn is_nan(self) -> bool {
|
|
2676
|
+
self.re.is_nan() || self.im.is_nan()
|
|
2677
|
+
}
|
|
2678
|
+
fn is_finite(self) -> bool {
|
|
2679
|
+
self.re.is_finite() && self.im.is_finite()
|
|
2680
|
+
}
|
|
2681
|
+
fn is_infinite(self) -> bool {
|
|
2682
|
+
self.re.is_infinite() || self.im.is_infinite()
|
|
2683
|
+
}
|
|
2684
|
+
fn has_infinity() -> bool {
|
|
2685
|
+
Scalar::has_infinity()
|
|
2686
|
+
}
|
|
2687
|
+
fn has_nan() -> bool {
|
|
2688
|
+
Scalar::has_nan()
|
|
2689
|
+
}
|
|
2690
|
+
fn has_subnormals() -> bool {
|
|
2691
|
+
Scalar::has_subnormals()
|
|
2692
|
+
}
|
|
2693
|
+
fn max_value() -> f32 {
|
|
2694
|
+
Scalar::max_value()
|
|
2695
|
+
}
|
|
2696
|
+
fn min_positive() -> f32 {
|
|
2697
|
+
Scalar::min_positive()
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2701
|
+
impl<Scalar: ComplexComponent> FloatConvertible for complex<Scalar> {
|
|
2702
|
+
type DimScalar = complex<Scalar>;
|
|
2703
|
+
type Unpacked = [complex<Scalar>; 1];
|
|
2704
|
+
#[inline(always)]
|
|
2705
|
+
fn unpack(self) -> [complex<Scalar>; 1] {
|
|
2706
|
+
[self]
|
|
2707
|
+
}
|
|
2708
|
+
#[inline(always)]
|
|
2709
|
+
fn pack(dims: [complex<Scalar>; 1]) -> Self {
|
|
2710
|
+
dims[0]
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
|
|
2714
|
+
// endregion: Complex Types
|
|
2715
|
+
|
|
2716
|
+
// region: FloatConvertible Trait
|
|
2717
|
+
|
|
2718
|
+
/// Trait for types that can unpack/pack logical sub-dimensions.
|
|
2719
|
+
///
|
|
2720
|
+
/// For normal scalar types (`dimensions_per_value() == 1`), `DimScalar = Self` and
|
|
2721
|
+
/// `Unpacked = [Self; 1]`. For packed sub-byte types, `DimScalar` is the natural
|
|
2722
|
+
/// scalar type for individual sub-dimensions (e.g., `i8` for `i4x2`).
|
|
2723
|
+
pub trait FloatConvertible: NumberLike {
|
|
2724
|
+
/// Scalar type for individual sub-dimensions.
|
|
2725
|
+
type DimScalar: Copy + Default + NumberLike;
|
|
2726
|
+
|
|
2727
|
+
/// Fixed-size array holding all unpacked sub-dimensions.
|
|
2728
|
+
type Unpacked: AsRef<[Self::DimScalar]> + AsMut<[Self::DimScalar]> + Copy + Default;
|
|
2729
|
+
|
|
2730
|
+
/// Unpack all logical sub-dimensions from this packed value.
|
|
2731
|
+
fn unpack(self) -> Self::Unpacked;
|
|
2732
|
+
|
|
2733
|
+
/// Pack sub-dimension scalars into a single storage value.
|
|
2734
|
+
fn pack(dims: Self::Unpacked) -> Self;
|
|
2735
|
+
}
|
|
2736
|
+
|
|
2737
|
+
impl FloatConvertible for f32 {
|
|
2738
|
+
type DimScalar = f32;
|
|
2739
|
+
type Unpacked = [f32; 1];
|
|
2740
|
+
#[inline(always)]
|
|
2741
|
+
fn unpack(self) -> [f32; 1] {
|
|
2742
|
+
[self]
|
|
2743
|
+
}
|
|
2744
|
+
#[inline(always)]
|
|
2745
|
+
fn pack(dims: [f32; 1]) -> Self {
|
|
2746
|
+
dims[0]
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2750
|
+
impl FloatConvertible for f64 {
|
|
2751
|
+
type DimScalar = f64;
|
|
2752
|
+
type Unpacked = [f64; 1];
|
|
2753
|
+
#[inline(always)]
|
|
2754
|
+
fn unpack(self) -> [f64; 1] {
|
|
2755
|
+
[self]
|
|
2756
|
+
}
|
|
2757
|
+
#[inline(always)]
|
|
2758
|
+
fn pack(dims: [f64; 1]) -> Self {
|
|
2759
|
+
dims[0]
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
|
|
2763
|
+
impl FloatConvertible for f16 {
|
|
2764
|
+
type DimScalar = f16;
|
|
2765
|
+
type Unpacked = [f16; 1];
|
|
2766
|
+
#[inline(always)]
|
|
2767
|
+
fn unpack(self) -> [f16; 1] {
|
|
2768
|
+
[self]
|
|
2769
|
+
}
|
|
2770
|
+
#[inline(always)]
|
|
2771
|
+
fn pack(dims: [f16; 1]) -> Self {
|
|
2772
|
+
dims[0]
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2776
|
+
impl FloatConvertible for bf16 {
|
|
2777
|
+
type DimScalar = bf16;
|
|
2778
|
+
type Unpacked = [bf16; 1];
|
|
2779
|
+
#[inline(always)]
|
|
2780
|
+
fn unpack(self) -> [bf16; 1] {
|
|
2781
|
+
[self]
|
|
2782
|
+
}
|
|
2783
|
+
#[inline(always)]
|
|
2784
|
+
fn pack(dims: [bf16; 1]) -> Self {
|
|
2785
|
+
dims[0]
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
|
|
2789
|
+
impl FloatConvertible for e4m3 {
|
|
2790
|
+
type DimScalar = e4m3;
|
|
2791
|
+
type Unpacked = [e4m3; 1];
|
|
2792
|
+
#[inline(always)]
|
|
2793
|
+
fn unpack(self) -> [e4m3; 1] {
|
|
2794
|
+
[self]
|
|
2795
|
+
}
|
|
2796
|
+
#[inline(always)]
|
|
2797
|
+
fn pack(dims: [e4m3; 1]) -> Self {
|
|
2798
|
+
dims[0]
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
impl FloatConvertible for e5m2 {
|
|
2803
|
+
type DimScalar = e5m2;
|
|
2804
|
+
type Unpacked = [e5m2; 1];
|
|
2805
|
+
#[inline(always)]
|
|
2806
|
+
fn unpack(self) -> [e5m2; 1] {
|
|
2807
|
+
[self]
|
|
2808
|
+
}
|
|
2809
|
+
#[inline(always)]
|
|
2810
|
+
fn pack(dims: [e5m2; 1]) -> Self {
|
|
2811
|
+
dims[0]
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
|
|
2815
|
+
impl FloatConvertible for e2m3 {
|
|
2816
|
+
type DimScalar = e2m3;
|
|
2817
|
+
type Unpacked = [e2m3; 1];
|
|
2818
|
+
#[inline(always)]
|
|
2819
|
+
fn unpack(self) -> [e2m3; 1] {
|
|
2820
|
+
[self]
|
|
2821
|
+
}
|
|
2822
|
+
#[inline(always)]
|
|
2823
|
+
fn pack(dims: [e2m3; 1]) -> Self {
|
|
2824
|
+
dims[0]
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2828
|
+
impl FloatConvertible for e3m2 {
|
|
2829
|
+
type DimScalar = e3m2;
|
|
2830
|
+
type Unpacked = [e3m2; 1];
|
|
2831
|
+
#[inline(always)]
|
|
2832
|
+
fn unpack(self) -> [e3m2; 1] {
|
|
2833
|
+
[self]
|
|
2834
|
+
}
|
|
2835
|
+
#[inline(always)]
|
|
2836
|
+
fn pack(dims: [e3m2; 1]) -> Self {
|
|
2837
|
+
dims[0]
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
|
|
2841
|
+
impl FloatConvertible for i8 {
|
|
2842
|
+
type DimScalar = i8;
|
|
2843
|
+
type Unpacked = [i8; 1];
|
|
2844
|
+
#[inline(always)]
|
|
2845
|
+
fn unpack(self) -> [i8; 1] {
|
|
2846
|
+
[self]
|
|
2847
|
+
}
|
|
2848
|
+
#[inline(always)]
|
|
2849
|
+
fn pack(dims: [i8; 1]) -> Self {
|
|
2850
|
+
dims[0]
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2854
|
+
impl FloatConvertible for u8 {
|
|
2855
|
+
type DimScalar = u8;
|
|
2856
|
+
type Unpacked = [u8; 1];
|
|
2857
|
+
#[inline(always)]
|
|
2858
|
+
fn unpack(self) -> [u8; 1] {
|
|
2859
|
+
[self]
|
|
2860
|
+
}
|
|
2861
|
+
#[inline(always)]
|
|
2862
|
+
fn pack(dims: [u8; 1]) -> Self {
|
|
2863
|
+
dims[0]
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2867
|
+
impl FloatConvertible for i16 {
|
|
2868
|
+
type DimScalar = i16;
|
|
2869
|
+
type Unpacked = [i16; 1];
|
|
2870
|
+
#[inline(always)]
|
|
2871
|
+
fn unpack(self) -> [i16; 1] {
|
|
2872
|
+
[self]
|
|
2873
|
+
}
|
|
2874
|
+
#[inline(always)]
|
|
2875
|
+
fn pack(dims: [i16; 1]) -> Self {
|
|
2876
|
+
dims[0]
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
impl FloatConvertible for u16 {
|
|
2881
|
+
type DimScalar = u16;
|
|
2882
|
+
type Unpacked = [u16; 1];
|
|
2883
|
+
#[inline(always)]
|
|
2884
|
+
fn unpack(self) -> [u16; 1] {
|
|
2885
|
+
[self]
|
|
2886
|
+
}
|
|
2887
|
+
#[inline(always)]
|
|
2888
|
+
fn pack(dims: [u16; 1]) -> Self {
|
|
2889
|
+
dims[0]
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
|
|
2893
|
+
impl FloatConvertible for i32 {
|
|
2894
|
+
type DimScalar = i32;
|
|
2895
|
+
type Unpacked = [i32; 1];
|
|
2896
|
+
#[inline(always)]
|
|
2897
|
+
fn unpack(self) -> [i32; 1] {
|
|
2898
|
+
[self]
|
|
2899
|
+
}
|
|
2900
|
+
#[inline(always)]
|
|
2901
|
+
fn pack(dims: [i32; 1]) -> Self {
|
|
2902
|
+
dims[0]
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2905
|
+
|
|
2906
|
+
impl FloatConvertible for u32 {
|
|
2907
|
+
type DimScalar = u32;
|
|
2908
|
+
type Unpacked = [u32; 1];
|
|
2909
|
+
#[inline(always)]
|
|
2910
|
+
fn unpack(self) -> [u32; 1] {
|
|
2911
|
+
[self]
|
|
2912
|
+
}
|
|
2913
|
+
#[inline(always)]
|
|
2914
|
+
fn pack(dims: [u32; 1]) -> Self {
|
|
2915
|
+
dims[0]
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
|
|
2919
|
+
impl FloatConvertible for i64 {
|
|
2920
|
+
type DimScalar = i64;
|
|
2921
|
+
type Unpacked = [i64; 1];
|
|
2922
|
+
#[inline(always)]
|
|
2923
|
+
fn unpack(self) -> [i64; 1] {
|
|
2924
|
+
[self]
|
|
2925
|
+
}
|
|
2926
|
+
#[inline(always)]
|
|
2927
|
+
fn pack(dims: [i64; 1]) -> Self {
|
|
2928
|
+
dims[0]
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2932
|
+
impl FloatConvertible for u64 {
|
|
2933
|
+
type DimScalar = u64;
|
|
2934
|
+
type Unpacked = [u64; 1];
|
|
2935
|
+
#[inline(always)]
|
|
2936
|
+
fn unpack(self) -> [u64; 1] {
|
|
2937
|
+
[self]
|
|
2938
|
+
}
|
|
2939
|
+
#[inline(always)]
|
|
2940
|
+
fn pack(dims: [u64; 1]) -> Self {
|
|
2941
|
+
dims[0]
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
|
|
2945
|
+
impl FloatConvertible for i4x2 {
|
|
2946
|
+
type DimScalar = i8;
|
|
2947
|
+
type Unpacked = [i8; 2];
|
|
2948
|
+
#[inline(always)]
|
|
2949
|
+
fn unpack(self) -> [i8; 2] {
|
|
2950
|
+
let (low, high) = self.to_i8s();
|
|
2951
|
+
[low, high]
|
|
2952
|
+
}
|
|
2953
|
+
#[inline(always)]
|
|
2954
|
+
fn pack(dims: [i8; 2]) -> Self {
|
|
2955
|
+
i4x2::from_i8s(dims[0], dims[1])
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
|
|
2959
|
+
impl FloatConvertible for u4x2 {
|
|
2960
|
+
type DimScalar = u8;
|
|
2961
|
+
type Unpacked = [u8; 2];
|
|
2962
|
+
#[inline(always)]
|
|
2963
|
+
fn unpack(self) -> [u8; 2] {
|
|
2964
|
+
let (low, high) = self.to_u8s();
|
|
2965
|
+
[low, high]
|
|
2966
|
+
}
|
|
2967
|
+
#[inline(always)]
|
|
2968
|
+
fn pack(dims: [u8; 2]) -> Self {
|
|
2969
|
+
u4x2::from_u8s(dims[0], dims[1])
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
|
|
2973
|
+
impl FloatConvertible for u1x8 {
|
|
2974
|
+
type DimScalar = u8;
|
|
2975
|
+
type Unpacked = [u8; 8];
|
|
2976
|
+
#[inline(always)]
|
|
2977
|
+
fn unpack(self) -> [u8; 8] {
|
|
2978
|
+
let mut out = [0u8; 8];
|
|
2979
|
+
for (i, slot) in out.iter_mut().enumerate() {
|
|
2980
|
+
*slot = (self.0 >> i) & 1;
|
|
2981
|
+
}
|
|
2982
|
+
out
|
|
2983
|
+
}
|
|
2984
|
+
#[inline(always)]
|
|
2985
|
+
fn pack(dims: [u8; 8]) -> Self {
|
|
2986
|
+
let mut byte = 0u8;
|
|
2987
|
+
for (i, &dim) in dims.iter().enumerate() {
|
|
2988
|
+
if dim != 0 {
|
|
2989
|
+
byte |= 1 << i;
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
u1x8(byte)
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
|
|
2996
|
+
// endregion: FloatConvertible Trait
|
|
2997
|
+
|
|
2998
|
+
// region: Dimension Proxies
|
|
2999
|
+
|
|
3000
|
+
/// Immutable proxy for a single logical dimension within a packed storage value.
|
|
3001
|
+
///
|
|
3002
|
+
/// For normal types this is equivalent to a `Scalar::DimScalar` copy.
|
|
3003
|
+
/// For sub-byte types it represents one unpacked dimension from a packed value.
|
|
3004
|
+
/// The lifetime ensures the proxy does not outlive the container it came from.
|
|
3005
|
+
pub struct DimRef<'a, Scalar: FloatConvertible> {
|
|
3006
|
+
value: Scalar::DimScalar,
|
|
3007
|
+
_marker: core::marker::PhantomData<&'a Scalar>,
|
|
3008
|
+
}
|
|
3009
|
+
|
|
3010
|
+
impl<'a, Scalar: FloatConvertible> DimRef<'a, Scalar> {
|
|
3011
|
+
/// Create a new immutable dimension proxy.
|
|
3012
|
+
#[inline]
|
|
3013
|
+
pub fn new(value: Scalar::DimScalar) -> Self {
|
|
3014
|
+
Self {
|
|
3015
|
+
value,
|
|
3016
|
+
_marker: core::marker::PhantomData,
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
|
|
3021
|
+
impl<Scalar: FloatConvertible> Copy for DimRef<'_, Scalar> {}
|
|
3022
|
+
|
|
3023
|
+
impl<Scalar: FloatConvertible> Clone for DimRef<'_, Scalar> {
|
|
3024
|
+
#[inline]
|
|
3025
|
+
fn clone(&self) -> Self {
|
|
3026
|
+
*self
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
|
|
3030
|
+
impl<Scalar: FloatConvertible> core::ops::Deref for DimRef<'_, Scalar> {
|
|
3031
|
+
type Target = Scalar::DimScalar;
|
|
3032
|
+
#[inline]
|
|
3033
|
+
fn deref(&self) -> &Scalar::DimScalar {
|
|
3034
|
+
&self.value
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
|
|
3038
|
+
impl<Scalar: FloatConvertible> PartialEq for DimRef<'_, Scalar>
|
|
3039
|
+
where
|
|
3040
|
+
Scalar::DimScalar: PartialEq,
|
|
3041
|
+
{
|
|
3042
|
+
#[inline]
|
|
3043
|
+
fn eq(&self, other: &Self) -> bool {
|
|
3044
|
+
self.value == other.value
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
|
|
3048
|
+
impl<Scalar: FloatConvertible> PartialOrd for DimRef<'_, Scalar>
|
|
3049
|
+
where
|
|
3050
|
+
Scalar::DimScalar: PartialOrd,
|
|
3051
|
+
{
|
|
3052
|
+
#[inline]
|
|
3053
|
+
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
|
3054
|
+
self.value.partial_cmp(&other.value)
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
|
|
3058
|
+
impl<Scalar: FloatConvertible> core::fmt::Debug for DimRef<'_, Scalar>
|
|
3059
|
+
where
|
|
3060
|
+
Scalar::DimScalar: core::fmt::Debug,
|
|
3061
|
+
{
|
|
3062
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
3063
|
+
self.value.fmt(f)
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
|
|
3067
|
+
impl<Scalar: FloatConvertible> core::fmt::Display for DimRef<'_, Scalar>
|
|
3068
|
+
where
|
|
3069
|
+
Scalar::DimScalar: core::fmt::Display,
|
|
3070
|
+
{
|
|
3071
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
3072
|
+
self.value.fmt(f)
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
|
|
3076
|
+
/// Mutable proxy for a single logical dimension within a packed storage value.
|
|
3077
|
+
///
|
|
3078
|
+
/// On [`Drop`], performs a read-modify-write: reads the current storage value,
|
|
3079
|
+
/// overwrites the sub-dimension at `sub_index`, and writes back.
|
|
3080
|
+
///
|
|
3081
|
+
/// For normal types (`dimensions_per_value() == 1`), this is equivalent to `&mut Scalar`.
|
|
3082
|
+
/// For sub-byte types, this enables modifying individual nibbles/bits.
|
|
3083
|
+
///
|
|
3084
|
+
/// # Aliasing
|
|
3085
|
+
///
|
|
3086
|
+
/// In a standard `for` loop each proxy is dropped before the next is created.
|
|
3087
|
+
/// Holding multiple proxies to sub-dimensions of the same storage value
|
|
3088
|
+
/// simultaneously is safe but uses last-writer-wins semantics.
|
|
3089
|
+
pub struct DimMut<'a, Scalar: FloatConvertible> {
|
|
3090
|
+
ptr: *mut Scalar,
|
|
3091
|
+
sub_index: usize,
|
|
3092
|
+
value: Scalar::DimScalar,
|
|
3093
|
+
_marker: core::marker::PhantomData<&'a mut Scalar>,
|
|
3094
|
+
}
|
|
3095
|
+
|
|
3096
|
+
impl<'a, Scalar: FloatConvertible> DimMut<'a, Scalar> {
|
|
3097
|
+
/// Create a new mutable dimension proxy.
|
|
3098
|
+
///
|
|
3099
|
+
/// # Safety
|
|
3100
|
+
///
|
|
3101
|
+
/// `ptr` must be valid for reads and writes for the duration of `'a`.
|
|
3102
|
+
#[inline]
|
|
3103
|
+
pub unsafe fn new(ptr: *mut Scalar, sub_index: usize, value: Scalar::DimScalar) -> Self {
|
|
3104
|
+
Self {
|
|
3105
|
+
ptr,
|
|
3106
|
+
sub_index,
|
|
3107
|
+
value,
|
|
3108
|
+
_marker: core::marker::PhantomData,
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
3112
|
+
|
|
3113
|
+
impl<Scalar: FloatConvertible> core::ops::Deref for DimMut<'_, Scalar> {
|
|
3114
|
+
type Target = Scalar::DimScalar;
|
|
3115
|
+
#[inline]
|
|
3116
|
+
fn deref(&self) -> &Scalar::DimScalar {
|
|
3117
|
+
&self.value
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
|
|
3121
|
+
impl<Scalar: FloatConvertible> core::ops::DerefMut for DimMut<'_, Scalar> {
|
|
3122
|
+
#[inline]
|
|
3123
|
+
fn deref_mut(&mut self) -> &mut Scalar::DimScalar {
|
|
3124
|
+
&mut self.value
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
|
|
3128
|
+
impl<Scalar: FloatConvertible> Drop for DimMut<'_, Scalar> {
|
|
3129
|
+
fn drop(&mut self) {
|
|
3130
|
+
let mut unpacked = unsafe { *self.ptr }.unpack();
|
|
3131
|
+
unpacked.as_mut()[self.sub_index] = self.value;
|
|
3132
|
+
unsafe { self.ptr.write(Scalar::pack(unpacked)) };
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
|
|
3136
|
+
impl<Scalar: FloatConvertible> core::fmt::Debug for DimMut<'_, Scalar>
|
|
3137
|
+
where
|
|
3138
|
+
Scalar::DimScalar: core::fmt::Debug,
|
|
3139
|
+
{
|
|
3140
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
3141
|
+
self.value.fmt(f)
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
|
|
3145
|
+
impl<Scalar: FloatConvertible> core::fmt::Display for DimMut<'_, Scalar>
|
|
3146
|
+
where
|
|
3147
|
+
Scalar::DimScalar: core::fmt::Display,
|
|
3148
|
+
{
|
|
3149
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
3150
|
+
self.value.fmt(f)
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
|
|
3154
|
+
// endregion: Dimension Proxies
|
|
3155
|
+
|
|
3156
|
+
// region: TestableType Trait (test-only)
|
|
3157
|
+
|
|
3158
|
+
#[cfg(test)]
|
|
3159
|
+
pub(crate) trait TestableType: FloatLike {
|
|
3160
|
+
/// Base absolute tolerance for this type.
|
|
3161
|
+
fn atol() -> f64;
|
|
3162
|
+
/// Base relative tolerance for this type.
|
|
3163
|
+
fn rtol() -> f64;
|
|
3164
|
+
}
|
|
3165
|
+
|
|
3166
|
+
#[cfg(test)]
|
|
3167
|
+
pub(crate) fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64, msg: &str) {
|
|
3168
|
+
let tol = atol + rtol * expected.abs();
|
|
3169
|
+
assert!(
|
|
3170
|
+
(actual - expected).abs() <= tol,
|
|
3171
|
+
"{}: expected {} but got {} (atol={}, rtol={}, tol={})",
|
|
3172
|
+
msg,
|
|
3173
|
+
expected,
|
|
3174
|
+
actual,
|
|
3175
|
+
atol,
|
|
3176
|
+
rtol,
|
|
3177
|
+
tol
|
|
3178
|
+
);
|
|
3179
|
+
}
|
|
3180
|
+
|
|
3181
|
+
#[cfg(test)]
|
|
3182
|
+
impl TestableType for f32 {
|
|
3183
|
+
fn atol() -> f64 {
|
|
3184
|
+
1e-4
|
|
3185
|
+
}
|
|
3186
|
+
fn rtol() -> f64 {
|
|
3187
|
+
1e-4
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
#[cfg(test)]
|
|
3191
|
+
impl TestableType for f64 {
|
|
3192
|
+
fn atol() -> f64 {
|
|
3193
|
+
1e-9
|
|
3194
|
+
}
|
|
3195
|
+
fn rtol() -> f64 {
|
|
3196
|
+
1e-9
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
#[cfg(test)]
|
|
3200
|
+
impl TestableType for f16 {
|
|
3201
|
+
fn atol() -> f64 {
|
|
3202
|
+
0.05
|
|
3203
|
+
}
|
|
3204
|
+
fn rtol() -> f64 {
|
|
3205
|
+
0.05
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
#[cfg(test)]
|
|
3209
|
+
impl TestableType for bf16 {
|
|
3210
|
+
fn atol() -> f64 {
|
|
3211
|
+
0.1
|
|
3212
|
+
}
|
|
3213
|
+
fn rtol() -> f64 {
|
|
3214
|
+
0.1
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
#[cfg(test)]
|
|
3218
|
+
impl TestableType for e4m3 {
|
|
3219
|
+
fn atol() -> f64 {
|
|
3220
|
+
0.5
|
|
3221
|
+
}
|
|
3222
|
+
fn rtol() -> f64 {
|
|
3223
|
+
0.1
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
#[cfg(test)]
|
|
3227
|
+
impl TestableType for e5m2 {
|
|
3228
|
+
fn atol() -> f64 {
|
|
3229
|
+
1.0
|
|
3230
|
+
}
|
|
3231
|
+
fn rtol() -> f64 {
|
|
3232
|
+
0.1
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
#[cfg(test)]
|
|
3236
|
+
impl TestableType for e2m3 {
|
|
3237
|
+
fn atol() -> f64 {
|
|
3238
|
+
0.5
|
|
3239
|
+
}
|
|
3240
|
+
fn rtol() -> f64 {
|
|
3241
|
+
0.1
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
#[cfg(test)]
|
|
3245
|
+
impl TestableType for e3m2 {
|
|
3246
|
+
fn atol() -> f64 {
|
|
3247
|
+
0.5
|
|
3248
|
+
}
|
|
3249
|
+
fn rtol() -> f64 {
|
|
3250
|
+
0.1
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
#[cfg(test)]
|
|
3254
|
+
impl TestableType for i8 {
|
|
3255
|
+
fn atol() -> f64 {
|
|
3256
|
+
1.0
|
|
3257
|
+
}
|
|
3258
|
+
fn rtol() -> f64 {
|
|
3259
|
+
0.0
|
|
3260
|
+
}
|
|
3261
|
+
}
|
|
3262
|
+
#[cfg(test)]
|
|
3263
|
+
impl TestableType for u8 {
|
|
3264
|
+
fn atol() -> f64 {
|
|
3265
|
+
1.0
|
|
3266
|
+
}
|
|
3267
|
+
fn rtol() -> f64 {
|
|
3268
|
+
0.0
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
#[cfg(test)]
|
|
3272
|
+
impl TestableType for i32 {
|
|
3273
|
+
fn atol() -> f64 {
|
|
3274
|
+
1.0
|
|
3275
|
+
}
|
|
3276
|
+
fn rtol() -> f64 {
|
|
3277
|
+
0.0
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3280
|
+
#[cfg(test)]
|
|
3281
|
+
impl TestableType for u32 {
|
|
3282
|
+
fn atol() -> f64 {
|
|
3283
|
+
1.0
|
|
3284
|
+
}
|
|
3285
|
+
fn rtol() -> f64 {
|
|
3286
|
+
0.0
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
#[cfg(test)]
|
|
3290
|
+
impl TestableType for i16 {
|
|
3291
|
+
fn atol() -> f64 {
|
|
3292
|
+
1.0
|
|
3293
|
+
}
|
|
3294
|
+
fn rtol() -> f64 {
|
|
3295
|
+
0.0
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
#[cfg(test)]
|
|
3299
|
+
impl TestableType for u16 {
|
|
3300
|
+
fn atol() -> f64 {
|
|
3301
|
+
1.0
|
|
3302
|
+
}
|
|
3303
|
+
fn rtol() -> f64 {
|
|
3304
|
+
0.0
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
#[cfg(test)]
|
|
3308
|
+
impl TestableType for i64 {
|
|
3309
|
+
fn atol() -> f64 {
|
|
3310
|
+
1.0
|
|
3311
|
+
}
|
|
3312
|
+
fn rtol() -> f64 {
|
|
3313
|
+
0.0
|
|
3314
|
+
}
|
|
3315
|
+
}
|
|
3316
|
+
#[cfg(test)]
|
|
3317
|
+
impl TestableType for u64 {
|
|
3318
|
+
fn atol() -> f64 {
|
|
3319
|
+
1.0
|
|
3320
|
+
}
|
|
3321
|
+
fn rtol() -> f64 {
|
|
3322
|
+
0.0
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
#[cfg(test)]
|
|
3326
|
+
impl TestableType for i4x2 {
|
|
3327
|
+
fn atol() -> f64 {
|
|
3328
|
+
1.0
|
|
3329
|
+
}
|
|
3330
|
+
fn rtol() -> f64 {
|
|
3331
|
+
0.0
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3334
|
+
#[cfg(test)]
|
|
3335
|
+
impl TestableType for u4x2 {
|
|
3336
|
+
fn atol() -> f64 {
|
|
3337
|
+
1.0
|
|
3338
|
+
}
|
|
3339
|
+
fn rtol() -> f64 {
|
|
3340
|
+
0.0
|
|
3341
|
+
}
|
|
3342
|
+
}
|
|
3343
|
+
#[cfg(test)]
|
|
3344
|
+
impl TestableType for u1x8 {
|
|
3345
|
+
fn atol() -> f64 {
|
|
3346
|
+
0.0
|
|
3347
|
+
}
|
|
3348
|
+
fn rtol() -> f64 {
|
|
3349
|
+
0.0
|
|
3350
|
+
}
|
|
3351
|
+
}
|
|
3352
|
+
|
|
3353
|
+
// endregion: TestableType Trait
|
|
3354
|
+
|
|
3355
|
+
#[cfg(test)]
|
|
3356
|
+
mod tests {
|
|
3357
|
+
use super::*;
|
|
3358
|
+
|
|
3359
|
+
fn assert_scalar_roundtrip<Scalar: FloatLike>(original: f32, abs_tol: f32, rel_tol: f32) {
|
|
3360
|
+
let converted = Scalar::from_f32(original);
|
|
3361
|
+
let roundtrip = NumberLike::to_f32(converted);
|
|
3362
|
+
if original == 0.0 {
|
|
3363
|
+
assert_eq!(roundtrip, 0.0, "Zero should roundtrip exactly");
|
|
3364
|
+
return;
|
|
3365
|
+
}
|
|
3366
|
+
let abs_error = f32_abs_compat(roundtrip - original);
|
|
3367
|
+
let rel_error = abs_error / f32_abs_compat(original);
|
|
3368
|
+
assert!(
|
|
3369
|
+
abs_error <= abs_tol || rel_error <= rel_tol,
|
|
3370
|
+
"Roundtrip failed for {}: got {} (abs_err={:.6}, rel_err={:.6})",
|
|
3371
|
+
original,
|
|
3372
|
+
roundtrip,
|
|
3373
|
+
abs_error,
|
|
3374
|
+
rel_error
|
|
3375
|
+
);
|
|
3376
|
+
}
|
|
3377
|
+
|
|
3378
|
+
fn assert_scalar_almost_equal<Scalar: FloatLike>(
|
|
3379
|
+
actual: f32,
|
|
3380
|
+
expected: f32,
|
|
3381
|
+
abs_tol: f32,
|
|
3382
|
+
rel_tol: f32,
|
|
3383
|
+
context: &str,
|
|
3384
|
+
) {
|
|
3385
|
+
let abs_error = f32_abs_compat(actual - expected);
|
|
3386
|
+
let rel_error = if expected != 0.0 {
|
|
3387
|
+
abs_error / f32_abs_compat(expected)
|
|
3388
|
+
} else {
|
|
3389
|
+
abs_error
|
|
3390
|
+
};
|
|
3391
|
+
assert!(
|
|
3392
|
+
abs_error <= abs_tol || rel_error <= rel_tol,
|
|
3393
|
+
"{}: expected {} but got {} (abs_err={:.6}, rel_err={:.6})",
|
|
3394
|
+
context,
|
|
3395
|
+
expected,
|
|
3396
|
+
actual,
|
|
3397
|
+
abs_error,
|
|
3398
|
+
rel_error
|
|
3399
|
+
);
|
|
3400
|
+
}
|
|
3401
|
+
|
|
3402
|
+
fn check_arithmetic<Scalar>(a_val: f32, b_val: f32, abs_tol: f32, rel_tol: f32)
|
|
3403
|
+
where
|
|
3404
|
+
Scalar: FloatLike
|
|
3405
|
+
+ PartialOrd
|
|
3406
|
+
+ PartialEq
|
|
3407
|
+
+ core::ops::Add<Output = Scalar>
|
|
3408
|
+
+ core::ops::Sub<Output = Scalar>
|
|
3409
|
+
+ core::ops::Mul<Output = Scalar>
|
|
3410
|
+
+ core::ops::Div<Output = Scalar>
|
|
3411
|
+
+ core::ops::Neg<Output = Scalar>,
|
|
3412
|
+
{
|
|
3413
|
+
let a = Scalar::from_f32(a_val);
|
|
3414
|
+
let b = Scalar::from_f32(b_val);
|
|
3415
|
+
assert_scalar_almost_equal::<Scalar>(
|
|
3416
|
+
NumberLike::to_f32(a + b),
|
|
3417
|
+
a_val + b_val,
|
|
3418
|
+
abs_tol,
|
|
3419
|
+
rel_tol,
|
|
3420
|
+
"add",
|
|
3421
|
+
);
|
|
3422
|
+
assert_scalar_almost_equal::<Scalar>(
|
|
3423
|
+
NumberLike::to_f32(a - b),
|
|
3424
|
+
a_val - b_val,
|
|
3425
|
+
abs_tol,
|
|
3426
|
+
rel_tol,
|
|
3427
|
+
"sub",
|
|
3428
|
+
);
|
|
3429
|
+
assert_scalar_almost_equal::<Scalar>(
|
|
3430
|
+
NumberLike::to_f32(a * b),
|
|
3431
|
+
a_val * b_val,
|
|
3432
|
+
abs_tol,
|
|
3433
|
+
rel_tol,
|
|
3434
|
+
"mul",
|
|
3435
|
+
);
|
|
3436
|
+
assert_scalar_almost_equal::<Scalar>(
|
|
3437
|
+
NumberLike::to_f32(a / b),
|
|
3438
|
+
a_val / b_val,
|
|
3439
|
+
abs_tol,
|
|
3440
|
+
rel_tol,
|
|
3441
|
+
"div",
|
|
3442
|
+
);
|
|
3443
|
+
assert_scalar_almost_equal::<Scalar>(
|
|
3444
|
+
NumberLike::to_f32(-a),
|
|
3445
|
+
-a_val,
|
|
3446
|
+
abs_tol,
|
|
3447
|
+
rel_tol,
|
|
3448
|
+
"neg",
|
|
3449
|
+
);
|
|
3450
|
+
assert_eq!(NumberLike::to_f32(Scalar::zero()), 0.0);
|
|
3451
|
+
assert_scalar_almost_equal::<Scalar>(
|
|
3452
|
+
NumberLike::to_f32(Scalar::one()),
|
|
3453
|
+
1.0,
|
|
3454
|
+
abs_tol,
|
|
3455
|
+
rel_tol,
|
|
3456
|
+
"ONE",
|
|
3457
|
+
);
|
|
3458
|
+
assert_scalar_almost_equal::<Scalar>(
|
|
3459
|
+
NumberLike::to_f32(Scalar::from_f32(-1.0)),
|
|
3460
|
+
-1.0,
|
|
3461
|
+
abs_tol,
|
|
3462
|
+
rel_tol,
|
|
3463
|
+
"NEG_ONE",
|
|
3464
|
+
);
|
|
3465
|
+
assert!(a > b);
|
|
3466
|
+
assert!(a == a);
|
|
3467
|
+
assert_scalar_almost_equal::<Scalar>(
|
|
3468
|
+
NumberLike::to_f32(NumberLike::abs(-a)),
|
|
3469
|
+
a_val,
|
|
3470
|
+
abs_tol,
|
|
3471
|
+
rel_tol,
|
|
3472
|
+
"abs",
|
|
3473
|
+
);
|
|
3474
|
+
assert!(NumberLike::is_finite(a));
|
|
3475
|
+
assert!(!NumberLike::is_nan(a));
|
|
3476
|
+
if Scalar::has_infinity() {
|
|
3477
|
+
assert!(!NumberLike::is_infinite(a));
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
|
|
3481
|
+
fn check_roundtrip<Scalar: FloatLike>(values: &[f32], abs_tol: f32, rel_tol: f32) {
|
|
3482
|
+
for &v in values {
|
|
3483
|
+
assert_scalar_roundtrip::<Scalar>(v, abs_tol, rel_tol);
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
|
|
3487
|
+
fn check_edge_cases<Scalar: FloatLike>() {
|
|
3488
|
+
assert_eq!(NumberLike::to_f32(Scalar::from_f32(0.0)), 0.0);
|
|
3489
|
+
assert_eq!(NumberLike::to_f32(Scalar::from_f32(-0.0)), 0.0);
|
|
3490
|
+
if Scalar::has_infinity() {
|
|
3491
|
+
assert!(NumberLike::to_f32(Scalar::from_f32(f32::INFINITY)).is_infinite());
|
|
3492
|
+
assert!(NumberLike::to_f32(Scalar::from_f32(f32::NEG_INFINITY)).is_infinite());
|
|
3493
|
+
} else {
|
|
3494
|
+
assert!(!NumberLike::to_f32(Scalar::from_f32(f32::INFINITY)).is_infinite());
|
|
3495
|
+
}
|
|
3496
|
+
if Scalar::has_nan() {
|
|
3497
|
+
assert!(NumberLike::to_f32(Scalar::from_f32(f32::NAN)).is_nan());
|
|
3498
|
+
} else {
|
|
3499
|
+
assert!(!NumberLike::to_f32(Scalar::from_f32(f32::NAN)).is_nan());
|
|
3500
|
+
}
|
|
3501
|
+
let big = Scalar::max_value() * 10.0;
|
|
3502
|
+
let overflow = Scalar::from_f32(big);
|
|
3503
|
+
if Scalar::has_infinity() {
|
|
3504
|
+
assert!(
|
|
3505
|
+
NumberLike::to_f32(overflow).is_infinite()
|
|
3506
|
+
|| NumberLike::to_f32(overflow) >= Scalar::max_value()
|
|
3507
|
+
);
|
|
3508
|
+
} else {
|
|
3509
|
+
let v = NumberLike::to_f32(overflow);
|
|
3510
|
+
assert!(!v.is_infinite() && !v.is_nan());
|
|
3511
|
+
assert!(v <= Scalar::max_value());
|
|
3512
|
+
let neg = NumberLike::to_f32(Scalar::from_f32(-big));
|
|
3513
|
+
assert!(!neg.is_infinite() && !neg.is_nan());
|
|
3514
|
+
assert!(neg >= -Scalar::max_value());
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
|
|
3518
|
+
fn check_subnormals<Scalar: FloatLike>(values: &[f32], upper_bound: f32) {
|
|
3519
|
+
for &val in values {
|
|
3520
|
+
let roundtrip = NumberLike::to_f32(Scalar::from_f32(val));
|
|
3521
|
+
assert!(
|
|
3522
|
+
roundtrip >= 0.0 && roundtrip < upper_bound,
|
|
3523
|
+
"{} subnormal test failed for {}: got {}",
|
|
3524
|
+
core::any::type_name::<Scalar>(),
|
|
3525
|
+
val,
|
|
3526
|
+
roundtrip
|
|
3527
|
+
);
|
|
3528
|
+
}
|
|
3529
|
+
}
|
|
3530
|
+
|
|
3531
|
+
#[test]
|
|
3532
|
+
fn arithmetic_ieee_halfs() {
|
|
3533
|
+
check_arithmetic::<f16>(3.5, 2.0, 0.002, 0.001);
|
|
3534
|
+
check_arithmetic::<bf16>(3.5, 2.0, 0.016, 0.008);
|
|
3535
|
+
}
|
|
3536
|
+
|
|
3537
|
+
#[test]
|
|
3538
|
+
fn arithmetic_minifloats() {
|
|
3539
|
+
check_arithmetic::<e4m3>(2.0, 1.5, 0.25, 0.125);
|
|
3540
|
+
check_arithmetic::<e5m2>(2.0, 1.5, 0.5, 0.25);
|
|
3541
|
+
check_arithmetic::<e2m3>(2.0, 1.5, 0.25, 0.125);
|
|
3542
|
+
check_arithmetic::<e3m2>(4.0, 2.0, 0.5, 0.25);
|
|
3543
|
+
}
|
|
3544
|
+
|
|
3545
|
+
#[test]
|
|
3546
|
+
fn roundtrip() {
|
|
3547
|
+
check_roundtrip::<f16>(
|
|
3548
|
+
&[
|
|
3549
|
+
0.0, 1.0, -1.0, 0.5, 2.0, 4.0, 8.0, 16.0, 100.0, 1000.0, 10000.0, 0.001, 0.0001,
|
|
3550
|
+
0.00001, -100.0, -1000.0,
|
|
3551
|
+
],
|
|
3552
|
+
0.002,
|
|
3553
|
+
0.001,
|
|
3554
|
+
);
|
|
3555
|
+
check_roundtrip::<bf16>(
|
|
3556
|
+
&[
|
|
3557
|
+
0.0, 1.0, -1.0, 0.5, 2.0, 10.0, 100.0, 1000.0, 1e6, 0.001, 1e-6, -100.0, -1000.0,
|
|
3558
|
+
],
|
|
3559
|
+
0.016,
|
|
3560
|
+
0.008,
|
|
3561
|
+
);
|
|
3562
|
+
check_roundtrip::<e4m3>(
|
|
3563
|
+
&[0.0, 1.0, -1.0, 0.5, 2.0, 4.0, 8.0, 16.0, 64.0, 128.0, 224.0],
|
|
3564
|
+
0.25,
|
|
3565
|
+
0.125,
|
|
3566
|
+
);
|
|
3567
|
+
check_roundtrip::<e5m2>(
|
|
3568
|
+
&[
|
|
3569
|
+
0.0, 1.0, -1.0, 0.5, 2.0, 4.0, 8.0, 16.0, 64.0, 256.0, 1024.0,
|
|
3570
|
+
],
|
|
3571
|
+
0.5,
|
|
3572
|
+
0.25,
|
|
3573
|
+
);
|
|
3574
|
+
check_roundtrip::<e2m3>(
|
|
3575
|
+
&[
|
|
3576
|
+
0.0, 1.0, -1.0, 0.5, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 7.5, 0.25, -0.25, 0.125, -0.125,
|
|
3577
|
+
-4.0, -6.0, -7.5,
|
|
3578
|
+
],
|
|
3579
|
+
0.25,
|
|
3580
|
+
0.125,
|
|
3581
|
+
);
|
|
3582
|
+
check_roundtrip::<e3m2>(
|
|
3583
|
+
&[
|
|
3584
|
+
0.0, 1.0, -1.0, 0.5, 2.0, 4.0, 8.0, 16.0, 20.0, 24.0, 28.0, 0.25, -0.25, -20.0,
|
|
3585
|
+
-28.0,
|
|
3586
|
+
],
|
|
3587
|
+
0.5,
|
|
3588
|
+
0.25,
|
|
3589
|
+
);
|
|
3590
|
+
}
|
|
3591
|
+
|
|
3592
|
+
#[test]
|
|
3593
|
+
fn edge_cases() {
|
|
3594
|
+
check_edge_cases::<f16>();
|
|
3595
|
+
check_edge_cases::<bf16>();
|
|
3596
|
+
check_edge_cases::<e4m3>();
|
|
3597
|
+
check_edge_cases::<e5m2>();
|
|
3598
|
+
check_edge_cases::<e2m3>();
|
|
3599
|
+
check_edge_cases::<e3m2>();
|
|
3600
|
+
}
|
|
3601
|
+
|
|
3602
|
+
#[test]
|
|
3603
|
+
fn subnormals() {
|
|
3604
|
+
check_subnormals::<f16>(&[1e-5, 1e-6, 1e-7, 5e-6, 5e-7], 1e-4);
|
|
3605
|
+
check_subnormals::<bf16>(&[1e-39, 1e-40, 1e-42], 1e-37);
|
|
3606
|
+
check_subnormals::<e4m3>(&[0.001, 0.0005], 0.002);
|
|
3607
|
+
check_subnormals::<e5m2>(&[0.00005, 0.00003, 0.00001], 0.0001);
|
|
3608
|
+
check_subnormals::<e2m3>(&[0.03, 0.015], 0.07);
|
|
3609
|
+
check_subnormals::<e3m2>(&[0.0625, 0.03], 0.15);
|
|
3610
|
+
}
|
|
3611
|
+
|
|
3612
|
+
#[test]
|
|
3613
|
+
fn half_crate_interop() {
|
|
3614
|
+
use half::bf16 as HalfBF16;
|
|
3615
|
+
use half::f16 as HalfF16;
|
|
3616
|
+
|
|
3617
|
+
// f16: all 65536 bit patterns
|
|
3618
|
+
for bits in 0u16..=u16::MAX {
|
|
3619
|
+
let half_val = HalfF16::from_bits(bits).to_f32();
|
|
3620
|
+
let nk_val = f16(bits).to_f32();
|
|
3621
|
+
assert!(
|
|
3622
|
+
half_val.to_bits() == nk_val.to_bits() || (half_val.is_nan() && nk_val.is_nan()),
|
|
3623
|
+
"f16 mismatch at bits 0x{bits:04X}: half={half_val}, numkong={nk_val}"
|
|
3624
|
+
);
|
|
3625
|
+
}
|
|
3626
|
+
|
|
3627
|
+
// bf16: all 65536 bit patterns
|
|
3628
|
+
for bits in 0u16..=u16::MAX {
|
|
3629
|
+
let half_val = HalfBF16::from_bits(bits).to_f32();
|
|
3630
|
+
let nk_val = bf16(bits).to_f32();
|
|
3631
|
+
assert!(
|
|
3632
|
+
half_val.to_bits() == nk_val.to_bits() || (half_val.is_nan() && nk_val.is_nan()),
|
|
3633
|
+
"bf16 mismatch at bits 0x{bits:04X}: half={half_val}, numkong={nk_val}"
|
|
3634
|
+
);
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
|
|
3638
|
+
#[test]
|
|
3639
|
+
fn is_close_exact() {
|
|
3640
|
+
assert!(is_close(1.0, 1.0, 0.0, 0.0));
|
|
3641
|
+
assert!(is_close(0.0, 0.0, 0.0, 0.0));
|
|
3642
|
+
assert!(is_close(-5.0, -5.0, 0.0, 0.0));
|
|
3643
|
+
}
|
|
3644
|
+
|
|
3645
|
+
#[test]
|
|
3646
|
+
fn is_close_within_atol() {
|
|
3647
|
+
assert!(is_close(1.0, 1.0 + 1e-7, 1e-6, 0.0));
|
|
3648
|
+
assert!(!is_close(1.0, 1.0 + 1e-5, 1e-6, 0.0));
|
|
3649
|
+
}
|
|
3650
|
+
|
|
3651
|
+
#[test]
|
|
3652
|
+
fn is_close_within_rtol() {
|
|
3653
|
+
assert!(is_close(100.0, 100.01, 0.0, 1e-3));
|
|
3654
|
+
assert!(!is_close(100.0, 100.2, 0.0, 1e-3));
|
|
3655
|
+
}
|
|
3656
|
+
|
|
3657
|
+
#[test]
|
|
3658
|
+
fn is_close_nan_inf() {
|
|
3659
|
+
// NaN is never close to anything (including itself).
|
|
3660
|
+
assert!(!is_close(f64::NAN, f64::NAN, 1e-6, 1e-6));
|
|
3661
|
+
assert!(!is_close(f64::NAN, 0.0, 1e-6, 1e-6));
|
|
3662
|
+
// INF - INF = NaN, so same-sign infinities are not "close" either.
|
|
3663
|
+
assert!(!is_close(f64::INFINITY, f64::INFINITY, 0.0, 0.0));
|
|
3664
|
+
assert!(!is_close(f64::INFINITY, f64::NEG_INFINITY, 0.0, 0.0));
|
|
3665
|
+
}
|
|
3666
|
+
}
|