usearch 2.10.3 → 2.11.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.
Files changed (114) hide show
  1. package/README.md +28 -8
  2. package/binding.gyp +101 -0
  3. package/c/CMakeLists.txt +27 -0
  4. package/c/README.md +244 -0
  5. package/c/lib.cpp +459 -0
  6. package/c/test.c +386 -0
  7. package/c/usearch.h +451 -0
  8. package/cmake/clipp.cmake +7 -0
  9. package/cmake/config.cmake.in +15 -0
  10. package/cmake/emscripten.cmake +8 -0
  11. package/cmake/jemalloc.cmake +62 -0
  12. package/cmake/pkg-config.pc.in +4 -0
  13. package/cmake/usearchConfigVersion.cmake.in +20 -0
  14. package/cpp/CMakeLists.txt +14 -0
  15. package/cpp/README.md +161 -0
  16. package/cpp/bench.cpp +616 -0
  17. package/cpp/test.cpp +335 -0
  18. package/fp16/CMakeLists.txt +176 -0
  19. package/fp16/LICENSE +11 -0
  20. package/fp16/README.md +20 -0
  21. package/fp16/bench/alt-element.cc +89 -0
  22. package/fp16/bench/from-alt-array.cc +175 -0
  23. package/fp16/bench/from-ieee-array.cc +399 -0
  24. package/fp16/bench/ieee-element.cc +272 -0
  25. package/fp16/bench/to-alt-array.cc +91 -0
  26. package/fp16/bench/to-ieee-array.cc +303 -0
  27. package/fp16/cmake/DownloadGoogleBenchmark.cmake +15 -0
  28. package/fp16/cmake/DownloadGoogleTest.cmake +15 -0
  29. package/fp16/cmake/DownloadPSimd.cmake +15 -0
  30. package/fp16/configure.py +65 -0
  31. package/fp16/confu.yaml +8 -0
  32. package/fp16/include/fp16/__init__.py +0 -0
  33. package/fp16/include/fp16/avx.py +50 -0
  34. package/fp16/include/fp16/avx2.py +53 -0
  35. package/fp16/include/fp16/bitcasts.h +92 -0
  36. package/fp16/include/fp16/fp16.h +451 -0
  37. package/fp16/include/fp16/psimd.h +131 -0
  38. package/fp16/include/fp16.h +11 -0
  39. package/fp16/jni/Android.mk +6 -0
  40. package/fp16/jni/Application.mk +4 -0
  41. package/fp16/test/alt-from-fp32-value.cc +498 -0
  42. package/fp16/test/alt-to-fp32-bits.cc +263 -0
  43. package/fp16/test/alt-to-fp32-psimd.cc +145 -0
  44. package/fp16/test/alt-to-fp32-value.cc +348 -0
  45. package/fp16/test/alt-to-fp32x2-psimd.cc +245 -0
  46. package/fp16/test/bitcasts.cc +56 -0
  47. package/fp16/test/ieee-from-fp32-value.cc +523 -0
  48. package/fp16/test/ieee-to-fp32-bits.cc +333 -0
  49. package/fp16/test/ieee-to-fp32-psimd.cc +312 -0
  50. package/fp16/test/ieee-to-fp32-value.cc +420 -0
  51. package/fp16/test/ieee-to-fp32x2-psimd.cc +541 -0
  52. package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +96 -0
  53. package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +96 -0
  54. package/fp16/test/peachpy/stubs.py +38 -0
  55. package/fp16/test/tables.cc +530 -0
  56. package/fp16/test/tables.h +13 -0
  57. package/fp16/third-party/THHalf.h +92 -0
  58. package/fp16/third-party/eigen-half.h +170 -0
  59. package/fp16/third-party/float16-compressor.h +80 -0
  60. package/fp16/third-party/half.hpp +2916 -0
  61. package/fp16/third-party/npy-halffloat.h +328 -0
  62. package/include/usearch/index.hpp +3838 -0
  63. package/include/usearch/index_dense.hpp +2022 -0
  64. package/include/usearch/index_plugins.hpp +2317 -0
  65. package/javascript/dist/cjs/usearch.js +5 -5
  66. package/javascript/dist/esm/usearch.js +2 -2
  67. package/javascript/dist-package-cjs.json +3 -0
  68. package/javascript/dist-package-esm.json +3 -0
  69. package/javascript/lib.cpp +350 -0
  70. package/javascript/node-gyp-build.d.ts +1 -0
  71. package/javascript/tsconfig-base.json +25 -0
  72. package/javascript/tsconfig-cjs.json +8 -0
  73. package/javascript/tsconfig-esm.json +8 -0
  74. package/javascript/usearch.test.js +84 -0
  75. package/javascript/usearch.ts +640 -0
  76. package/package.json +14 -7
  77. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  78. package/prebuilds/linux-arm64/usearch.node +0 -0
  79. package/prebuilds/linux-x64/usearch.node +0 -0
  80. package/prebuilds/win32-ia32/usearch.node +0 -0
  81. package/prebuilds/win32-x64/usearch.node +0 -0
  82. package/simsimd/.releaserc +87 -0
  83. package/simsimd/CONTRIBUTING.md +114 -0
  84. package/simsimd/Cargo.lock +709 -0
  85. package/simsimd/Cargo.toml +44 -0
  86. package/simsimd/LICENSE +201 -0
  87. package/simsimd/README.md +708 -0
  88. package/simsimd/binding.gyp +36 -0
  89. package/simsimd/build.rs +51 -0
  90. package/simsimd/c/lib.c +110 -0
  91. package/simsimd/include/simsimd/binary.h +258 -0
  92. package/simsimd/include/simsimd/dot.h +1237 -0
  93. package/simsimd/include/simsimd/geospatial.h +37 -0
  94. package/simsimd/include/simsimd/probability.h +563 -0
  95. package/simsimd/include/simsimd/simsimd.h +1129 -0
  96. package/simsimd/include/simsimd/spatial.h +1019 -0
  97. package/simsimd/include/simsimd/types.h +309 -0
  98. package/simsimd/javascript/bench.js +127 -0
  99. package/simsimd/javascript/dist-package-cjs.json +3 -0
  100. package/simsimd/javascript/dist-package-esm.json +3 -0
  101. package/simsimd/javascript/fallback.ts +203 -0
  102. package/simsimd/javascript/lib.c +107 -0
  103. package/simsimd/javascript/node-gyp-build.d.ts +1 -0
  104. package/simsimd/javascript/simsimd.ts +135 -0
  105. package/simsimd/javascript/test.mjs +179 -0
  106. package/simsimd/javascript/tsconfig-base.json +37 -0
  107. package/simsimd/javascript/tsconfig-cjs.json +8 -0
  108. package/simsimd/javascript/tsconfig-esm.json +8 -0
  109. package/simsimd/package-lock.json +1709 -0
  110. package/simsimd/package.json +61 -0
  111. package/simsimd/rust/benches/cosine.rs +27 -0
  112. package/simsimd/rust/benches/native.rs +30 -0
  113. package/simsimd/rust/benches/sqeuclidean.rs +27 -0
  114. package/simsimd/rust/lib.rs +778 -0
@@ -0,0 +1,523 @@
1
+ #include <gtest/gtest.h>
2
+
3
+ #include <cstdint>
4
+
5
+ #include <fp16.h>
6
+ #include <tables.h>
7
+
8
+ #if (defined(__i386__) || defined(__x86_64__)) && defined(__F16C__)
9
+ #include <x86intrin.h>
10
+ #endif
11
+
12
+
13
+ TEST(FP16_IEEE_FROM_FP32_VALUE, normalized_powers_of_2) {
14
+ const uint16_t min_po2_f16 = UINT16_C(0x0400);
15
+ const uint16_t eighths_f16 = UINT16_C(0x3000);
16
+ const uint16_t quarter_f16 = UINT16_C(0x3400);
17
+ const uint16_t half_f16 = UINT16_C(0x3800);
18
+ const uint16_t one_f16 = UINT16_C(0x3C00);
19
+ const uint16_t two_f16 = UINT16_C(0x4000);
20
+ const uint16_t four_f16 = UINT16_C(0x4400);
21
+ const uint16_t eight_f16 = UINT16_C(0x4800);
22
+ const uint16_t sixteen_f16 = UINT16_C(0x4C00);
23
+ const uint16_t thirtytwo_f16 = UINT16_C(0x5000);
24
+ const uint16_t sixtyfour_f16 = UINT16_C(0x5400);
25
+ const uint16_t max_po2_f16 = UINT16_C(0x7800);
26
+
27
+ const uint32_t min_po2_f32 = UINT32_C(0x38800000);
28
+ const uint32_t eighths_f32 = UINT32_C(0x3E000000);
29
+ const uint32_t quarter_f32 = UINT32_C(0x3E800000);
30
+ const uint32_t half_f32 = UINT32_C(0x3F000000);
31
+ const uint32_t one_f32 = UINT32_C(0x3F800000);
32
+ const uint32_t two_f32 = UINT32_C(0x40000000);
33
+ const uint32_t four_f32 = UINT32_C(0x40800000);
34
+ const uint32_t eight_f32 = UINT32_C(0x41000000);
35
+ const uint32_t sixteen_f32 = UINT32_C(0x41800000);
36
+ const uint32_t thirtytwo_f32 = UINT32_C(0x42000000);
37
+ const uint32_t sixtyfour_f32 = UINT32_C(0x42800000);
38
+ const uint32_t max_po2_f32 = UINT32_C(0x47000000);
39
+
40
+ float min_po2_value;
41
+ memcpy(&min_po2_value, &min_po2_f32, sizeof(min_po2_value));
42
+ EXPECT_EQ(min_po2_f16, fp16_ieee_from_fp32_value(min_po2_value)) <<
43
+ std::hex << std::uppercase << std::setfill('0') <<
44
+ "F32 = 0x" << std::setw(8) << min_po2_f32 << ", " <<
45
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(min_po2_value) << ", " <<
46
+ "F16 = 0x" << std::setw(4) << min_po2_f16;
47
+
48
+ float eighths_value;
49
+ memcpy(&eighths_value, &eighths_f32, sizeof(eighths_value));
50
+ EXPECT_EQ(eighths_f16, fp16_ieee_from_fp32_value(eighths_value)) <<
51
+ std::hex << std::uppercase << std::setfill('0') <<
52
+ "F32 = 0x" << std::setw(8) << eighths_f32 << ", " <<
53
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(eighths_value) << ", " <<
54
+ "F16 = 0x" << std::setw(4) << eighths_f16;
55
+
56
+ float quarter_value;
57
+ memcpy(&quarter_value, &quarter_f32, sizeof(quarter_value));
58
+ EXPECT_EQ(quarter_f16, fp16_ieee_from_fp32_value(quarter_value)) <<
59
+ std::hex << std::uppercase << std::setfill('0') <<
60
+ "F32 = 0x" << std::setw(8) << quarter_f32 << ", " <<
61
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(quarter_value) << ", " <<
62
+ "F16 = 0x" << std::setw(4) << quarter_f16;
63
+
64
+ float half_value;
65
+ memcpy(&half_value, &half_f32, sizeof(half_value));
66
+ EXPECT_EQ(half_f16, fp16_ieee_from_fp32_value(half_value)) <<
67
+ std::hex << std::uppercase << std::setfill('0') <<
68
+ "F32 = 0x" << std::setw(8) << half_f32 << ", " <<
69
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(half_value) << ", " <<
70
+ "F16 = 0x" << std::setw(4) << half_f16;
71
+
72
+ float one_value;
73
+ memcpy(&one_value, &one_f32, sizeof(one_value));
74
+ EXPECT_EQ(one_f16, fp16_ieee_from_fp32_value(one_value)) <<
75
+ std::hex << std::uppercase << std::setfill('0') <<
76
+ "F32 = 0x" << std::setw(8) << one_f32 << ", " <<
77
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(one_value) << ", " <<
78
+ "F16 = 0x" << std::setw(4) << one_f16;
79
+
80
+ float two_value;
81
+ memcpy(&two_value, &two_f32, sizeof(two_value));
82
+ EXPECT_EQ(two_f16, fp16_ieee_from_fp32_value(two_value)) <<
83
+ std::hex << std::uppercase << std::setfill('0') <<
84
+ "F32 = 0x" << std::setw(8) << two_f32 << ", " <<
85
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(two_value) << ", " <<
86
+ "F16 = 0x" << std::setw(4) << two_f16;
87
+
88
+ float four_value;
89
+ memcpy(&four_value, &four_f32, sizeof(four_value));
90
+ EXPECT_EQ(four_f16, fp16_ieee_from_fp32_value(four_value)) <<
91
+ std::hex << std::uppercase << std::setfill('0') <<
92
+ "F32 = 0x" << std::setw(8) << four_f32 << ", " <<
93
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(four_value) << ", " <<
94
+ "F16 = 0x" << std::setw(4) << four_f16;
95
+
96
+ float eight_value;
97
+ memcpy(&eight_value, &eight_f32, sizeof(eight_value));
98
+ EXPECT_EQ(eight_f16, fp16_ieee_from_fp32_value(eight_value)) <<
99
+ std::hex << std::uppercase << std::setfill('0') <<
100
+ "F32 = 0x" << std::setw(8) << eight_f32 << ", " <<
101
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(eight_value) << ", " <<
102
+ "F16 = 0x" << std::setw(4) << eight_f16;
103
+
104
+ float sixteen_value;
105
+ memcpy(&sixteen_value, &sixteen_f32, sizeof(sixteen_value));
106
+ EXPECT_EQ(sixteen_f16, fp16_ieee_from_fp32_value(sixteen_value)) <<
107
+ std::hex << std::uppercase << std::setfill('0') <<
108
+ "F32 = 0x" << std::setw(8) << sixteen_f32 << ", " <<
109
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(sixteen_value) << ", " <<
110
+ "F16 = 0x" << std::setw(4) << sixteen_f16;
111
+
112
+ float thirtytwo_value;
113
+ memcpy(&thirtytwo_value, &thirtytwo_f32, sizeof(thirtytwo_value));
114
+ EXPECT_EQ(thirtytwo_f16, fp16_ieee_from_fp32_value(thirtytwo_value)) <<
115
+ std::hex << std::uppercase << std::setfill('0') <<
116
+ "F32 = 0x" << std::setw(8) << thirtytwo_f32 << ", " <<
117
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(thirtytwo_value) << ", " <<
118
+ "F16 = 0x" << std::setw(4) << thirtytwo_f16;
119
+
120
+ float sixtyfour_value;
121
+ memcpy(&sixtyfour_value, &sixtyfour_f32, sizeof(sixtyfour_value));
122
+ EXPECT_EQ(sixtyfour_f16, fp16_ieee_from_fp32_value(sixtyfour_value)) <<
123
+ std::hex << std::uppercase << std::setfill('0') <<
124
+ "F32 = 0x" << std::setw(8) << sixtyfour_f32 << ", " <<
125
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(sixtyfour_value) << ", " <<
126
+ "F16 = 0x" << std::setw(4) << sixtyfour_f16;
127
+
128
+ float max_po2_value;
129
+ memcpy(&max_po2_value, &max_po2_f32, sizeof(max_po2_value));
130
+ EXPECT_EQ(max_po2_f16, fp16_ieee_from_fp32_value(max_po2_value)) <<
131
+ std::hex << std::uppercase << std::setfill('0') <<
132
+ "F32 = 0x" << std::setw(8) << max_po2_f32 << ", " <<
133
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(max_po2_value) << ", " <<
134
+ "F16 = 0x" << std::setw(4) << max_po2_f16;
135
+ }
136
+
137
+ TEST(FP16_IEEE_FROM_FP32_VALUE, denormalized_powers_of_2) {
138
+ const uint16_t exp2_minus_15_f16 = UINT16_C(0x0200);
139
+ const uint16_t exp2_minus_16_f16 = UINT16_C(0x0100);
140
+ const uint16_t exp2_minus_17_f16 = UINT16_C(0x0080);
141
+ const uint16_t exp2_minus_18_f16 = UINT16_C(0x0040);
142
+ const uint16_t exp2_minus_19_f16 = UINT16_C(0x0020);
143
+ const uint16_t exp2_minus_20_f16 = UINT16_C(0x0010);
144
+ const uint16_t exp2_minus_21_f16 = UINT16_C(0x0008);
145
+ const uint16_t exp2_minus_22_f16 = UINT16_C(0x0004);
146
+ const uint16_t exp2_minus_23_f16 = UINT16_C(0x0002);
147
+ const uint16_t exp2_minus_24_f16 = UINT16_C(0x0001);
148
+ const uint16_t exp2_minus_25_f16 = UINT16_C(0x0000);
149
+
150
+ const uint32_t exp2_minus_15_f32 = UINT32_C(0x38000000);
151
+ const uint32_t exp2_minus_16_f32 = UINT32_C(0x37800000);
152
+ const uint32_t exp2_minus_17_f32 = UINT32_C(0x37000000);
153
+ const uint32_t exp2_minus_18_f32 = UINT32_C(0x36800000);
154
+ const uint32_t exp2_minus_19_f32 = UINT32_C(0x36000000);
155
+ const uint32_t exp2_minus_20_f32 = UINT32_C(0x35800000);
156
+ const uint32_t exp2_minus_21_f32 = UINT32_C(0x35000000);
157
+ const uint32_t exp2_minus_22_f32 = UINT32_C(0x34800000);
158
+ const uint32_t exp2_minus_23_f32 = UINT32_C(0x34000000);
159
+ const uint32_t exp2_minus_24_f32 = UINT32_C(0x33800000);
160
+ const uint32_t exp2_minus_25_f32 = UINT32_C(0x33000000);
161
+
162
+ float exp2_minus_15_value;
163
+ memcpy(&exp2_minus_15_value, &exp2_minus_15_f32, sizeof(exp2_minus_15_value));
164
+ EXPECT_EQ(exp2_minus_15_f16, fp16_ieee_from_fp32_value(exp2_minus_15_value)) <<
165
+ std::hex << std::uppercase << std::setfill('0') <<
166
+ "F32 = 0x" << std::setw(8) << exp2_minus_15_f32 << ", " <<
167
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_15_value) << ", " <<
168
+ "F16 = 0x" << std::setw(4) << exp2_minus_15_f16;
169
+
170
+ float exp2_minus_16_value;
171
+ memcpy(&exp2_minus_16_value, &exp2_minus_16_f32, sizeof(exp2_minus_16_value));
172
+ EXPECT_EQ(exp2_minus_16_f16, fp16_ieee_from_fp32_value(exp2_minus_16_value)) <<
173
+ std::hex << std::uppercase << std::setfill('0') <<
174
+ "F32 = 0x" << std::setw(8) << exp2_minus_16_f32 << ", " <<
175
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_16_value) << ", " <<
176
+ "F16 = 0x" << std::setw(4) << exp2_minus_16_f16;
177
+
178
+ float exp2_minus_17_value;
179
+ memcpy(&exp2_minus_17_value, &exp2_minus_17_f32, sizeof(exp2_minus_17_value));
180
+ EXPECT_EQ(exp2_minus_17_f16, fp16_ieee_from_fp32_value(exp2_minus_17_value)) <<
181
+ std::hex << std::uppercase << std::setfill('0') <<
182
+ "F32 = 0x" << std::setw(8) << exp2_minus_17_f32 << ", " <<
183
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_17_value) << ", " <<
184
+ "F16 = 0x" << std::setw(4) << exp2_minus_17_f16;
185
+
186
+ float exp2_minus_18_value;
187
+ memcpy(&exp2_minus_18_value, &exp2_minus_18_f32, sizeof(exp2_minus_18_value));
188
+ EXPECT_EQ(exp2_minus_18_f16, fp16_ieee_from_fp32_value(exp2_minus_18_value)) <<
189
+ std::hex << std::uppercase << std::setfill('0') <<
190
+ "F32 = 0x" << std::setw(8) << exp2_minus_18_f32 << ", " <<
191
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_18_value) << ", " <<
192
+ "F16 = 0x" << std::setw(4) << exp2_minus_18_f16;
193
+
194
+ float exp2_minus_19_value;
195
+ memcpy(&exp2_minus_19_value, &exp2_minus_19_f32, sizeof(exp2_minus_19_value));
196
+ EXPECT_EQ(exp2_minus_19_f16, fp16_ieee_from_fp32_value(exp2_minus_19_value)) <<
197
+ std::hex << std::uppercase << std::setfill('0') <<
198
+ "F32 = 0x" << std::setw(8) << exp2_minus_19_f32 << ", " <<
199
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_19_value) << ", " <<
200
+ "F16 = 0x" << std::setw(4) << exp2_minus_19_f16;
201
+
202
+ float exp2_minus_20_value;
203
+ memcpy(&exp2_minus_20_value, &exp2_minus_20_f32, sizeof(exp2_minus_20_value));
204
+ EXPECT_EQ(exp2_minus_20_f16, fp16_ieee_from_fp32_value(exp2_minus_20_value)) <<
205
+ std::hex << std::uppercase << std::setfill('0') <<
206
+ "F32 = 0x" << std::setw(8) << exp2_minus_20_f32 << ", " <<
207
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_20_value) << ", " <<
208
+ "F16 = 0x" << std::setw(4) << exp2_minus_20_f16;
209
+
210
+ float exp2_minus_21_value;
211
+ memcpy(&exp2_minus_21_value, &exp2_minus_21_f32, sizeof(exp2_minus_21_value));
212
+ EXPECT_EQ(exp2_minus_21_f16, fp16_ieee_from_fp32_value(exp2_minus_21_value)) <<
213
+ std::hex << std::uppercase << std::setfill('0') <<
214
+ "F32 = 0x" << std::setw(8) << exp2_minus_21_f32 << ", " <<
215
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_21_value) << ", " <<
216
+ "F16 = 0x" << std::setw(4) << exp2_minus_21_f16;
217
+
218
+ float exp2_minus_22_value;
219
+ memcpy(&exp2_minus_22_value, &exp2_minus_22_f32, sizeof(exp2_minus_22_value));
220
+ EXPECT_EQ(exp2_minus_22_f16, fp16_ieee_from_fp32_value(exp2_minus_22_value)) <<
221
+ std::hex << std::uppercase << std::setfill('0') <<
222
+ "F32 = 0x" << std::setw(8) << exp2_minus_22_f32 << ", " <<
223
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_22_value) << ", " <<
224
+ "F16 = 0x" << std::setw(4) << exp2_minus_22_f16;
225
+
226
+ float exp2_minus_23_value;
227
+ memcpy(&exp2_minus_23_value, &exp2_minus_23_f32, sizeof(exp2_minus_23_value));
228
+ EXPECT_EQ(exp2_minus_23_f16, fp16_ieee_from_fp32_value(exp2_minus_23_value)) <<
229
+ std::hex << std::uppercase << std::setfill('0') <<
230
+ "F32 = 0x" << std::setw(8) << exp2_minus_23_f32 << ", " <<
231
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_23_value) << ", " <<
232
+ "F16 = 0x" << std::setw(4) << exp2_minus_23_f16;
233
+
234
+ float exp2_minus_24_value;
235
+ memcpy(&exp2_minus_24_value, &exp2_minus_24_f32, sizeof(exp2_minus_24_value));
236
+ EXPECT_EQ(exp2_minus_24_f16, fp16_ieee_from_fp32_value(exp2_minus_24_value)) <<
237
+ std::hex << std::uppercase << std::setfill('0') <<
238
+ "F32 = 0x" << std::setw(8) << exp2_minus_24_f32 << ", " <<
239
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_24_value) << ", " <<
240
+ "F16 = 0x" << std::setw(4) << exp2_minus_24_f16;
241
+
242
+ float exp2_minus_25_value;
243
+ memcpy(&exp2_minus_25_value, &exp2_minus_25_f32, sizeof(exp2_minus_25_value));
244
+ EXPECT_EQ(exp2_minus_25_f16, fp16_ieee_from_fp32_value(exp2_minus_25_value)) <<
245
+ std::hex << std::uppercase << std::setfill('0') <<
246
+ "F32 = 0x" << std::setw(8) << exp2_minus_25_f32 << ", " <<
247
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_25_value) << ", " <<
248
+ "F16 = 0x" << std::setw(4) << exp2_minus_25_f16;
249
+ }
250
+
251
+ TEST(FP16_IEEE_FROM_FP32_VALUE, zero) {
252
+ const uint16_t positive_zero_f16 = UINT16_C(0x0000);
253
+ const uint16_t negative_zero_f16 = UINT16_C(0x8000);
254
+
255
+ const uint32_t positive_zero_f32 = UINT32_C(0x00000000);
256
+ const uint32_t negative_zero_f32 = UINT32_C(0x80000000);
257
+
258
+ float positive_zero_value;
259
+ memcpy(&positive_zero_value, &positive_zero_f32, sizeof(positive_zero_value));
260
+ EXPECT_EQ(positive_zero_f16, fp16_ieee_from_fp32_value(positive_zero_value)) <<
261
+ std::hex << std::uppercase << std::setfill('0') <<
262
+ "F32 = 0x" << std::setw(8) << positive_zero_f32 << ", " <<
263
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(positive_zero_value) << ", " <<
264
+ "F16 = 0x" << std::setw(4) << positive_zero_f16;
265
+
266
+ float negative_zero_value;
267
+ memcpy(&negative_zero_value, &negative_zero_f32, sizeof(negative_zero_value));
268
+ EXPECT_EQ(negative_zero_f16, fp16_ieee_from_fp32_value(negative_zero_value)) <<
269
+ std::hex << std::uppercase << std::setfill('0') <<
270
+ "F32 = 0x" << std::setw(8) << negative_zero_f32 << ", " <<
271
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(negative_zero_value) << ", " <<
272
+ "F16 = 0x" << std::setw(4) << negative_zero_f16;
273
+ }
274
+
275
+ TEST(FP16_IEEE_FROM_FP32_VALUE, infinity) {
276
+ const uint16_t positive_infinity_f16 = UINT16_C(0x7C00);
277
+ const uint16_t negative_infinity_f16 = UINT16_C(0xFC00);
278
+
279
+ const uint32_t positive_infinity_f32 = UINT32_C(0x7F800000);
280
+ const uint32_t negative_infinity_f32 = UINT32_C(0xFF800000);
281
+
282
+ float positive_infinity_value;
283
+ memcpy(&positive_infinity_value, &positive_infinity_f32, sizeof(positive_infinity_value));
284
+ EXPECT_EQ(positive_infinity_f16, fp16_ieee_from_fp32_value(positive_infinity_value)) <<
285
+ std::hex << std::uppercase << std::setfill('0') <<
286
+ "F32 = 0x" << std::setw(8) << positive_infinity_f32 << ", " <<
287
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(positive_infinity_value) << ", " <<
288
+ "F16 = 0x" << std::setw(4) << positive_infinity_f16;
289
+
290
+ float negative_infinity_value;
291
+ memcpy(&negative_infinity_value, &negative_infinity_f32, sizeof(negative_infinity_value));
292
+ EXPECT_EQ(negative_infinity_f16, fp16_ieee_from_fp32_value(negative_infinity_value)) <<
293
+ std::hex << std::uppercase << std::setfill('0') <<
294
+ "F32 = 0x" << std::setw(8) << negative_infinity_f32 << ", " <<
295
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(negative_infinity_value) << ", " <<
296
+ "F16 = 0x" << std::setw(4) << negative_infinity_f16;
297
+ }
298
+
299
+ TEST(FP16_IEEE_FROM_FP32_VALUE, positive_nan) {
300
+ for (uint32_t nan_f32 = UINT32_C(0x7FFFFFFF); nan_f32 > UINT32_C(0x7F800000); nan_f32--) {
301
+ float nan_value;
302
+ memcpy(&nan_value, &nan_f32, sizeof(nan_value));
303
+ const uint16_t nan_f16 = fp16_ieee_from_fp32_value(nan_value);
304
+
305
+ /* Check sign */
306
+ ASSERT_EQ(nan_f16 & UINT16_C(0x8000), 0) <<
307
+ std::hex << std::uppercase << std::setfill('0') <<
308
+ "F32 = 0x" << std::setw(8) << nan_f32 << ", " <<
309
+ "F16(F32) = 0x" << std::setw(4) << nan_f16;
310
+
311
+ /* Check exponent */
312
+ ASSERT_EQ(nan_f16 & UINT16_C(0x7C00), UINT16_C(0x7C00)) <<
313
+ std::hex << std::uppercase << std::setfill('0') <<
314
+ "F32 = 0x" << std::setw(8) << nan_f32 << ", " <<
315
+ "F16(F32) = 0x" << std::setw(4) << nan_f16;
316
+
317
+ /* Check mantissa */
318
+ ASSERT_NE(nan_f16 & UINT16_C(0x03FF), 0) <<
319
+ std::hex << std::uppercase << std::setfill('0') <<
320
+ "F32 = 0x" << std::setw(8) << nan_f32 << ", " <<
321
+ "F16(F32) = 0x" << std::setw(4) << nan_f16;
322
+ }
323
+ }
324
+
325
+ TEST(FP16_IEEE_FROM_FP32_VALUE, negative_nan) {
326
+ for (uint32_t nan_f32 = UINT32_C(0xFFFFFFFF); nan_f32 > UINT32_C(0xFF800000); nan_f32--) {
327
+ float nan_value;
328
+ memcpy(&nan_value, &nan_f32, sizeof(nan_value));
329
+ const uint16_t nan_f16 = fp16_ieee_from_fp32_value(nan_value);
330
+
331
+ /* Check sign */
332
+ ASSERT_EQ(nan_f16 & UINT16_C(0x8000), UINT16_C(0x8000)) <<
333
+ std::hex << std::uppercase << std::setfill('0') <<
334
+ "F32 = 0x" << std::setw(8) << nan_f32 << ", " <<
335
+ "F16(F32) = 0x" << std::setw(4) << nan_f16;
336
+
337
+ /* Check exponent */
338
+ ASSERT_EQ(nan_f16 & UINT16_C(0x7C00), UINT16_C(0x7C00)) <<
339
+ std::hex << std::uppercase << std::setfill('0') <<
340
+ "F32 = 0x" << std::setw(8) << nan_f32 << ", " <<
341
+ "F16(F32) = 0x" << std::setw(4) << nan_f16;
342
+
343
+ /* Check mantissa */
344
+ ASSERT_NE(nan_f16 & UINT16_C(0x03FF), 0) <<
345
+ std::hex << std::uppercase << std::setfill('0') <<
346
+ "F32 = 0x" << std::setw(8) << nan_f32 << ", " <<
347
+ "F16(F32) = 0x" << std::setw(4) << nan_f16;
348
+ }
349
+ }
350
+
351
+ TEST(FP16_IEEE_FROM_FP32_VALUE, revertible) {
352
+ /* Positive values */
353
+ for (uint16_t f16 = UINT16_C(0x0000); f16 < UINT16_C(0x7C00); f16++) {
354
+ const float value_f32 = fp16_ieee_to_fp32_value(f16);
355
+ uint32_t bits_f32;
356
+ memcpy(&bits_f32, &value_f32, sizeof(bits_f32));
357
+
358
+ ASSERT_EQ(f16, fp16_ieee_from_fp32_value(value_f32)) <<
359
+ std::hex << std::uppercase << std::setfill('0') <<
360
+ "F16 = 0x" << std::setw(4) << f16 << ", " <<
361
+ "F32(F16) = 0x" << std::setw(8) << bits_f32 << ", " <<
362
+ "F16(F32(F16)) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value_f32);
363
+ }
364
+
365
+ /* Negative values */
366
+ for (uint16_t f16 = UINT16_C(0x8000); f16 < UINT16_C(0xFC00); f16++) {
367
+ const float value_f32 = fp16_ieee_to_fp32_value(f16);
368
+ uint32_t bits_f32;
369
+ memcpy(&bits_f32, &value_f32, sizeof(bits_f32));
370
+
371
+ ASSERT_EQ(f16, fp16_ieee_from_fp32_value(value_f32)) <<
372
+ std::hex << std::uppercase << std::setfill('0') <<
373
+ "F16 = 0x" << std::setw(4) << f16 << ", " <<
374
+ "F32(F16) = 0x" << std::setw(8) << bits_f32 << ", " <<
375
+ "F16(F32(F16)) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value_f32);
376
+ }
377
+ }
378
+
379
+ TEST(FP16_IEEE_FROM_FP32_VALUE, underflow) {
380
+ const uint32_t min_nonzero_f32 = UINT32_C(0x33000001);
381
+ const uint16_t zero_f16 = UINT16_C(0x0000);
382
+ const uint16_t min_f16 = UINT16_C(0x0001);
383
+ for (uint32_t bits = UINT32_C(0x00000001); bits < min_nonzero_f32; bits++) {
384
+ float value;
385
+ memcpy(&value, &bits, sizeof(value));
386
+ ASSERT_EQ(zero_f16, fp16_ieee_from_fp32_value(value)) <<
387
+ std::hex << std::uppercase << std::setfill('0') <<
388
+ "F32 = 0x" << std::setw(8) << bits << ", " <<
389
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " <<
390
+ "F16 = 0x" << std::setw(4) << zero_f16;
391
+ }
392
+ float min_nonzero_value;
393
+ memcpy(&min_nonzero_value, &min_nonzero_f32, sizeof(min_nonzero_value));
394
+ ASSERT_EQ(min_f16, fp16_ieee_from_fp32_value(min_nonzero_value)) <<
395
+ std::hex << std::uppercase << std::setfill('0') <<
396
+ "F32 = 0x" << std::setw(8) << min_nonzero_f32 << ", " <<
397
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(min_nonzero_value) << ", " <<
398
+ "F16 = 0x" << std::setw(4) << min_f16;
399
+ }
400
+
401
+ TEST(FP16_IEEE_FROM_FP32_VALUE, overflow) {
402
+ const uint32_t max_finite_f32 = UINT32_C(0x477FEFFF);
403
+ const uint16_t max_finite_f16 = UINT16_C(0x7BFF);
404
+ const uint32_t positive_infinity_f32 = UINT32_C(0x7F800000);
405
+ const uint16_t positive_infinity_f16 = UINT16_C(0x7C00);
406
+ for (uint32_t bits = positive_infinity_f32; bits > max_finite_f32; bits--) {
407
+ float value;
408
+ memcpy(&value, &bits, sizeof(value));
409
+ ASSERT_EQ(positive_infinity_f16, fp16_ieee_from_fp32_value(value)) <<
410
+ std::hex << std::uppercase << std::setfill('0') <<
411
+ "F32 = 0x" << std::setw(8) << bits << ", " <<
412
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " <<
413
+ "F16 = 0x" << std::setw(4) << positive_infinity_f16;
414
+ }
415
+ float max_finite_value;
416
+ memcpy(&max_finite_value, &max_finite_f32, sizeof(max_finite_value));
417
+ ASSERT_EQ(max_finite_f16, fp16_ieee_from_fp32_value(max_finite_value)) <<
418
+ std::hex << std::uppercase << std::setfill('0') <<
419
+ "F32 = 0x" << std::setw(8) << max_finite_f32 << ", " <<
420
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(max_finite_value) << ", " <<
421
+ "F16 = 0x" << std::setw(4) << max_finite_f16;
422
+ }
423
+
424
+ TEST(FP16_IEEE_FROM_FP32_VALUE, positive_denormalized_values) {
425
+ const uint32_t min_nonzero_f32 = UINT32_C(0x33000001);
426
+
427
+ uint32_t f32_begin = min_nonzero_f32;
428
+ for (uint16_t f16 = 0; f16 < UINT16_C(0x0400); f16++) {
429
+ const uint32_t f32_end = fp16::denormalizedRanges[f16];
430
+ for (uint32_t f32 = f32_begin; f32 < f32_end; f32++) {
431
+ float value;
432
+ memcpy(&value, &f32, sizeof(value));
433
+ ASSERT_EQ(f16, fp16_ieee_from_fp32_value(value)) <<
434
+ std::hex << std::uppercase << std::setfill('0') <<
435
+ "F32 = 0x" << std::setw(8) << f32 << ", " <<
436
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " <<
437
+ "F16 = 0x" << std::setw(4) << f16;
438
+ }
439
+ f32_begin = f32_end;
440
+ }
441
+ }
442
+
443
+ TEST(FP16_IEEE_FROM_FP32_VALUE, negative_denormalized_values) {
444
+ const uint32_t min_nonzero_f32 = UINT32_C(0x33000001);
445
+
446
+ uint32_t f32_begin = min_nonzero_f32 | UINT32_C(0x80000000);
447
+ for (uint16_t f16 = UINT16_C(0x8000); f16 < UINT16_C(0x8400); f16++) {
448
+ const uint32_t f32_end = fp16::denormalizedRanges[f16 & UINT16_C(0x7FFF)] | UINT32_C(0x80000000);
449
+ for (uint32_t f32 = f32_begin; f32 < f32_end; f32++) {
450
+ float value;
451
+ memcpy(&value, &f32, sizeof(value));
452
+ ASSERT_EQ(f16, fp16_ieee_from_fp32_value(value)) <<
453
+ std::hex << std::uppercase << std::setfill('0') <<
454
+ "F32 = 0x" << std::setw(8) << f32 << ", " <<
455
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " <<
456
+ "F16 = 0x" << std::setw(4) << f16;
457
+ }
458
+ f32_begin = f32_end;
459
+ }
460
+ }
461
+
462
+ TEST(FP16_IEEE_FROM_FP32_VALUE, positive_normalized_values) {
463
+ /* Minimum number that rounds to 1.0h when converted to half-precision */
464
+ const uint32_t min_one_f32 = UINT32_C(0x3F7FF000);
465
+ const uint32_t e_bias = 15;
466
+
467
+ for (int32_t e = -14; e <= 15; e++) {
468
+ uint32_t f32_begin = min_one_f32 + (uint32_t(e) << 23);
469
+ for (uint16_t f16 = uint16_t(e + e_bias) << 10; f16 < uint16_t(e + e_bias + 1) << 10; f16++) {
470
+ const uint32_t f32_end = fp16::normalizedRanges[f16 & UINT16_C(0x3FF)] + (uint32_t(e) << 23);
471
+ for (uint32_t f32 = f32_begin; f32 < f32_end; f32++) {
472
+ float value;
473
+ memcpy(&value, &f32, sizeof(value));
474
+ ASSERT_EQ(f16, fp16_ieee_from_fp32_value(value)) <<
475
+ std::hex << std::uppercase << std::setfill('0') <<
476
+ "F32 = 0x" << std::setw(8) << f32 << ", " <<
477
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " <<
478
+ "F16 = 0x" << std::setw(4) << f16;
479
+ }
480
+ f32_begin = f32_end;
481
+ }
482
+ }
483
+ }
484
+
485
+ TEST(FP16_IEEE_FROM_FP32_VALUE, negative_normalized_values) {
486
+ /* Minimum number that rounds to 1.0h when converted to half-precision */
487
+ const uint32_t min_one_f32 = UINT32_C(0x3F7FF000);
488
+ const uint32_t e_bias = 15;
489
+
490
+ for (int32_t e = -14; e <= 15; e++) {
491
+ uint32_t f32_begin = (min_one_f32 | UINT32_C(0x80000000)) + (uint32_t(e) << 23);
492
+ for (uint16_t f16 = (UINT16_C(0x8000) | (uint16_t(e + e_bias) << 10)); f16 < (UINT16_C(0x8000) | (uint16_t(e + e_bias + 1) << 10)); f16++) {
493
+ const uint32_t f32_end = (fp16::normalizedRanges[f16 & UINT16_C(0x3FF)] | UINT32_C(0x80000000)) + (uint32_t(e) << 23);
494
+ for (uint32_t f32 = f32_begin; f32 < f32_end; f32++) {
495
+ float value;
496
+ memcpy(&value, &f32, sizeof(value));
497
+ ASSERT_EQ(f16, fp16_ieee_from_fp32_value(value)) <<
498
+ std::hex << std::uppercase << std::setfill('0') <<
499
+ "F32 = 0x" << std::setw(8) << f32 << ", " <<
500
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " <<
501
+ "F16 = 0x" << std::setw(4) << f16;
502
+ }
503
+ f32_begin = f32_end;
504
+ }
505
+ }
506
+ }
507
+
508
+ #if (defined(__i386__) || defined(__x86_64__)) && defined(__F16C__)
509
+ TEST(FP16_IEEE_FROM_FP32_VALUE, match_hardware) {
510
+ const uint32_t min_nonzero = UINT32_C(0x00000001);
511
+ const uint32_t max_finite = UINT32_C(0x7F800000);
512
+ for (uint32_t bits = min_nonzero; bits < max_finite; bits++) {
513
+ float value;
514
+ memcpy(&value, &bits, sizeof(value));
515
+ const uint16_t reference = uint16_t(_mm_cvtsi128_si32(_mm_cvtps_ph(_mm_set_ss(value), _MM_FROUND_CUR_DIRECTION)));
516
+ ASSERT_EQ(reference, fp16_ieee_from_fp32_value(value)) <<
517
+ std::hex << std::uppercase << std::setfill('0') <<
518
+ "F32 = 0x" << std::setw(8) << bits << ", " <<
519
+ "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " <<
520
+ "F16 = 0x" << std::setw(4) << reference;
521
+ }
522
+ }
523
+ #endif