usearch 2.20.3 → 2.20.5
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 +1 -1
- package/include/usearch/index.hpp +1 -1
- package/package.json +1 -1
- 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/simsimd/Cargo.lock +201 -197
- package/simsimd/Cargo.toml +8 -4
- package/simsimd/README.md +114 -48
- package/simsimd/c/lib.c +16 -0
- package/simsimd/include/simsimd/simsimd.h +2 -2
- package/simsimd/include/simsimd/types.h +42 -4
- package/simsimd/package-lock.json +2 -2
- package/simsimd/package.json +1 -1
- package/simsimd/rust/lib.rs +439 -16
package/simsimd/rust/lib.rs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
//! # SpatialSimilarity - Hardware-Accelerated Similarity Metrics and Distance Functions
|
|
2
2
|
//!
|
|
3
3
|
//! * Targets ARM NEON, SVE, x86 AVX2, AVX-512 (VNNI, FP16) hardware backends.
|
|
4
|
-
//! * Handles `f64` double
|
|
4
|
+
//! * Handles `f64` double- and `f32` single-precision, integral, and binary vectors.
|
|
5
|
+
//! * Exposes half-precision (`f16`) and brain floating point (`bf16`) types.
|
|
5
6
|
//! * Zero-dependency header-only C 99 library with bindings for Rust and other languages.
|
|
6
7
|
//!
|
|
7
8
|
//! ## Implemented distance functions include:
|
|
@@ -26,11 +27,32 @@
|
|
|
26
27
|
//!
|
|
27
28
|
//! // Compute squared Euclidean distance
|
|
28
29
|
//! let l2sq_dist = i8::l2sq(a, b);
|
|
29
|
-
//!
|
|
30
|
+
//!
|
|
30
31
|
//! // Optimize performance by flushing denormals
|
|
31
32
|
//! simsimd::capabilities::flush_denormals();
|
|
32
33
|
//! ```
|
|
33
34
|
//!
|
|
35
|
+
//! ## Mixed Precision Support
|
|
36
|
+
//!
|
|
37
|
+
//! ```rust
|
|
38
|
+
//! use simsimd::{SpatialSimilarity, f16, bf16};
|
|
39
|
+
//!
|
|
40
|
+
//! // Work with half-precision floats
|
|
41
|
+
//! let half_a: Vec<f16> = vec![1.0, 2.0, 3.0].iter().map(|&x| f16::from_f32(x)).collect();
|
|
42
|
+
//! let half_b: Vec<f16> = vec![4.0, 5.0, 6.0].iter().map(|&x| f16::from_f32(x)).collect();
|
|
43
|
+
//! let half_cos = f16::cos(&half_a, &half_b);
|
|
44
|
+
//!
|
|
45
|
+
//! // Work with brain floats
|
|
46
|
+
//! let brain_a: Vec<bf16> = vec![1.0, 2.0, 3.0].iter().map(|&x| bf16::from_f32(x)).collect();
|
|
47
|
+
//! let brain_b: Vec<bf16> = vec![4.0, 5.0, 6.0].iter().map(|&x| bf16::from_f32(x)).collect();
|
|
48
|
+
//! let brain_cos = bf16::cos(&brain_a, &brain_b);
|
|
49
|
+
//!
|
|
50
|
+
//! // Direct bit manipulation
|
|
51
|
+
//! let half = f16::from_f32(3.14);
|
|
52
|
+
//! let bits = half.0; // Access raw u16 representation
|
|
53
|
+
//! let reconstructed = f16(bits);
|
|
54
|
+
//! ```
|
|
55
|
+
//!
|
|
34
56
|
//! ## Traits
|
|
35
57
|
//!
|
|
36
58
|
//! The `SpatialSimilarity` trait covers following methods:
|
|
@@ -50,10 +72,12 @@
|
|
|
50
72
|
//! - `kullbackleibler(a: &[Self], b: &[Self]) -> Option<Distance>`: Computes Kullback-Leibler divergence between two slices.
|
|
51
73
|
//!
|
|
52
74
|
#![allow(non_camel_case_types)]
|
|
75
|
+
#![cfg_attr(not(test), no_std)]
|
|
53
76
|
|
|
54
|
-
type Distance = f64;
|
|
55
|
-
type ComplexProduct = (f64, f64);
|
|
77
|
+
pub type Distance = f64;
|
|
78
|
+
pub type ComplexProduct = (f64, f64);
|
|
56
79
|
|
|
80
|
+
#[link(name = "simsimd")]
|
|
57
81
|
extern "C" {
|
|
58
82
|
|
|
59
83
|
fn simsimd_dot_i8(a: *const i8, b: *const i8, c: usize, d: *mut Distance);
|
|
@@ -136,19 +160,360 @@ extern "C" {
|
|
|
136
160
|
|
|
137
161
|
fn simsimd_flush_denormals() -> i32;
|
|
138
162
|
fn simsimd_uses_dynamic_dispatch() -> i32;
|
|
163
|
+
|
|
164
|
+
fn simsimd_f32_to_f16(f32_value: f32, result_ptr: *mut u16);
|
|
165
|
+
fn simsimd_f16_to_f32(f16_ptr: *const u16) -> f32;
|
|
166
|
+
fn simsimd_f32_to_bf16(f32_value: f32, result_ptr: *mut u16);
|
|
167
|
+
fn simsimd_bf16_to_f32(bf16_ptr: *const u16) -> f32;
|
|
139
168
|
}
|
|
140
169
|
|
|
141
|
-
/// A half-precision floating point number.
|
|
170
|
+
/// A half-precision (16-bit) floating point number.
|
|
171
|
+
///
|
|
172
|
+
/// This type represents IEEE 754 half-precision binary floating-point format.
|
|
173
|
+
/// It provides conversion methods to and from f32, and the underlying u16
|
|
174
|
+
/// representation is publicly accessible for direct bit manipulation.
|
|
175
|
+
///
|
|
176
|
+
/// # Examples
|
|
177
|
+
///
|
|
178
|
+
/// ```
|
|
179
|
+
/// use simsimd::f16;
|
|
180
|
+
///
|
|
181
|
+
/// // Create from f32
|
|
182
|
+
/// let half = f16::from_f32(3.14);
|
|
183
|
+
///
|
|
184
|
+
/// // Convert back to f32
|
|
185
|
+
/// let float = half.to_f32();
|
|
186
|
+
///
|
|
187
|
+
/// // Direct access to bits
|
|
188
|
+
/// let bits = half.0;
|
|
189
|
+
/// ```
|
|
142
190
|
#[repr(transparent)]
|
|
143
|
-
|
|
191
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
192
|
+
pub struct f16(pub u16);
|
|
193
|
+
|
|
194
|
+
impl f16 {
|
|
195
|
+
/// Positive zero.
|
|
196
|
+
pub const ZERO: Self = f16(0);
|
|
197
|
+
|
|
198
|
+
/// Positive one.
|
|
199
|
+
pub const ONE: Self = f16(0x3C00);
|
|
200
|
+
|
|
201
|
+
/// Negative one.
|
|
202
|
+
pub const NEG_ONE: Self = f16(0xBC00);
|
|
203
|
+
|
|
204
|
+
/// Converts an f32 to f16 representation.
|
|
205
|
+
///
|
|
206
|
+
/// # Examples
|
|
207
|
+
///
|
|
208
|
+
/// ```
|
|
209
|
+
/// use simsimd::f16;
|
|
210
|
+
/// let half = f16::from_f32(3.14159);
|
|
211
|
+
/// ```
|
|
212
|
+
#[inline(always)]
|
|
213
|
+
pub fn from_f32(value: f32) -> Self {
|
|
214
|
+
let mut result: u16 = 0;
|
|
215
|
+
unsafe { simsimd_f32_to_f16(value, &mut result) };
|
|
216
|
+
f16(result)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/// Converts the f16 to an f32.
|
|
220
|
+
///
|
|
221
|
+
/// # Examples
|
|
222
|
+
///
|
|
223
|
+
/// ```
|
|
224
|
+
/// use simsimd::f16;
|
|
225
|
+
/// let half = f16::from_f32(3.14159);
|
|
226
|
+
/// let float = half.to_f32();
|
|
227
|
+
/// ```
|
|
228
|
+
#[inline(always)]
|
|
229
|
+
pub fn to_f32(self) -> f32 {
|
|
230
|
+
unsafe { simsimd_f16_to_f32(&self.0) }
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/// Returns true if this value is NaN.
|
|
234
|
+
#[inline(always)]
|
|
235
|
+
pub fn is_nan(self) -> bool {
|
|
236
|
+
self.to_f32().is_nan()
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/// Returns true if this value is positive or negative infinity.
|
|
240
|
+
#[inline(always)]
|
|
241
|
+
pub fn is_infinite(self) -> bool {
|
|
242
|
+
self.to_f32().is_infinite()
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/// Returns true if this number is neither infinite nor NaN.
|
|
246
|
+
#[inline(always)]
|
|
247
|
+
pub fn is_finite(self) -> bool {
|
|
248
|
+
self.to_f32().is_finite()
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/// Returns the absolute value of self.
|
|
252
|
+
#[inline(always)]
|
|
253
|
+
pub fn abs(self) -> Self {
|
|
254
|
+
Self::from_f32(self.to_f32().abs())
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/// Returns the largest integer less than or equal to a number.
|
|
258
|
+
///
|
|
259
|
+
/// This method is only available when the `std` feature is enabled.
|
|
260
|
+
#[cfg(feature = "std")]
|
|
261
|
+
#[inline(always)]
|
|
262
|
+
pub fn floor(self) -> Self {
|
|
263
|
+
Self::from_f32(self.to_f32().floor())
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/// Returns the smallest integer greater than or equal to a number.
|
|
267
|
+
///
|
|
268
|
+
/// This method is only available when the `std` feature is enabled.
|
|
269
|
+
#[cfg(feature = "std")]
|
|
270
|
+
#[inline(always)]
|
|
271
|
+
pub fn ceil(self) -> Self {
|
|
272
|
+
Self::from_f32(self.to_f32().ceil())
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/// Returns the nearest integer to a number. Round half-way cases away from 0.0.
|
|
276
|
+
///
|
|
277
|
+
/// This method is only available when the `std` feature is enabled.
|
|
278
|
+
#[cfg(feature = "std")]
|
|
279
|
+
#[inline(always)]
|
|
280
|
+
pub fn round(self) -> Self {
|
|
281
|
+
Self::from_f32(self.to_f32().round())
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
#[cfg(feature = "std")]
|
|
286
|
+
impl std::fmt::Display for f16 {
|
|
287
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
288
|
+
write!(f, "{}", self.to_f32())
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
impl core::ops::Add for f16 {
|
|
293
|
+
type Output = Self;
|
|
294
|
+
|
|
295
|
+
#[inline(always)]
|
|
296
|
+
fn add(self, rhs: Self) -> Self::Output {
|
|
297
|
+
Self::from_f32(self.to_f32() + rhs.to_f32())
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
impl core::ops::Sub for f16 {
|
|
302
|
+
type Output = Self;
|
|
144
303
|
|
|
145
|
-
|
|
304
|
+
#[inline(always)]
|
|
305
|
+
fn sub(self, rhs: Self) -> Self::Output {
|
|
306
|
+
Self::from_f32(self.to_f32() - rhs.to_f32())
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
impl core::ops::Mul for f16 {
|
|
311
|
+
type Output = Self;
|
|
312
|
+
|
|
313
|
+
#[inline(always)]
|
|
314
|
+
fn mul(self, rhs: Self) -> Self::Output {
|
|
315
|
+
Self::from_f32(self.to_f32() * rhs.to_f32())
|
|
316
|
+
}
|
|
317
|
+
}
|
|
146
318
|
|
|
147
|
-
|
|
319
|
+
impl core::ops::Div for f16 {
|
|
320
|
+
type Output = Self;
|
|
321
|
+
|
|
322
|
+
#[inline(always)]
|
|
323
|
+
fn div(self, rhs: Self) -> Self::Output {
|
|
324
|
+
Self::from_f32(self.to_f32() / rhs.to_f32())
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
impl core::ops::Neg for f16 {
|
|
329
|
+
type Output = Self;
|
|
330
|
+
|
|
331
|
+
#[inline(always)]
|
|
332
|
+
fn neg(self) -> Self::Output {
|
|
333
|
+
Self::from_f32(-self.to_f32())
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
impl core::cmp::PartialOrd for f16 {
|
|
338
|
+
#[inline(always)]
|
|
339
|
+
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
|
340
|
+
self.to_f32().partial_cmp(&other.to_f32())
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/// A brain floating point (bfloat16) number.
|
|
345
|
+
///
|
|
346
|
+
/// This type represents Google's bfloat16 format, which truncates IEEE 754
|
|
347
|
+
/// single-precision to 16 bits by keeping the exponent bits but reducing
|
|
348
|
+
/// the mantissa. This provides a wider range than f16 but lower precision.
|
|
349
|
+
///
|
|
350
|
+
/// # Examples
|
|
351
|
+
///
|
|
352
|
+
/// ```
|
|
353
|
+
/// use simsimd::bf16;
|
|
354
|
+
///
|
|
355
|
+
/// // Create from f32
|
|
356
|
+
/// let brain_half = bf16::from_f32(3.14);
|
|
357
|
+
///
|
|
358
|
+
/// // Convert back to f32
|
|
359
|
+
/// let float = brain_half.to_f32();
|
|
360
|
+
///
|
|
361
|
+
/// // Direct access to bits
|
|
362
|
+
/// let bits = brain_half.0;
|
|
363
|
+
/// ```
|
|
148
364
|
#[repr(transparent)]
|
|
149
|
-
|
|
365
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
366
|
+
pub struct bf16(pub u16);
|
|
367
|
+
|
|
368
|
+
impl bf16 {
|
|
369
|
+
/// Positive zero.
|
|
370
|
+
pub const ZERO: Self = bf16(0);
|
|
371
|
+
|
|
372
|
+
/// Positive one.
|
|
373
|
+
pub const ONE: Self = bf16(0x3F80);
|
|
374
|
+
|
|
375
|
+
/// Negative one.
|
|
376
|
+
pub const NEG_ONE: Self = bf16(0xBF80);
|
|
377
|
+
|
|
378
|
+
/// Converts an f32 to bf16 representation.
|
|
379
|
+
///
|
|
380
|
+
/// # Examples
|
|
381
|
+
///
|
|
382
|
+
/// ```
|
|
383
|
+
/// use simsimd::bf16;
|
|
384
|
+
/// let brain_half = bf16::from_f32(3.14159);
|
|
385
|
+
/// ```
|
|
386
|
+
#[inline(always)]
|
|
387
|
+
pub fn from_f32(value: f32) -> Self {
|
|
388
|
+
let mut result: u16 = 0;
|
|
389
|
+
unsafe { simsimd_f32_to_bf16(value, &mut result) };
|
|
390
|
+
bf16(result)
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/// Converts the bf16 to an f32.
|
|
394
|
+
///
|
|
395
|
+
/// # Examples
|
|
396
|
+
///
|
|
397
|
+
/// ```
|
|
398
|
+
/// use simsimd::bf16;
|
|
399
|
+
/// let brain_half = bf16::from_f32(3.14159);
|
|
400
|
+
/// let float = brain_half.to_f32();
|
|
401
|
+
/// ```
|
|
402
|
+
#[inline(always)]
|
|
403
|
+
pub fn to_f32(self) -> f32 {
|
|
404
|
+
unsafe { simsimd_bf16_to_f32(&self.0) }
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/// Returns true if this value is NaN.
|
|
408
|
+
#[inline(always)]
|
|
409
|
+
pub fn is_nan(self) -> bool {
|
|
410
|
+
self.to_f32().is_nan()
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/// Returns true if this value is positive or negative infinity.
|
|
414
|
+
#[inline(always)]
|
|
415
|
+
pub fn is_infinite(self) -> bool {
|
|
416
|
+
self.to_f32().is_infinite()
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/// Returns true if this number is neither infinite nor NaN.
|
|
420
|
+
#[inline(always)]
|
|
421
|
+
pub fn is_finite(self) -> bool {
|
|
422
|
+
self.to_f32().is_finite()
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/// Returns the absolute value of self.
|
|
426
|
+
#[inline(always)]
|
|
427
|
+
pub fn abs(self) -> Self {
|
|
428
|
+
Self::from_f32(self.to_f32().abs())
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/// Returns the largest integer less than or equal to a number.
|
|
432
|
+
///
|
|
433
|
+
/// This method is only available when the `std` feature is enabled.
|
|
434
|
+
#[cfg(feature = "std")]
|
|
435
|
+
#[inline(always)]
|
|
436
|
+
pub fn floor(self) -> Self {
|
|
437
|
+
Self::from_f32(self.to_f32().floor())
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/// Returns the smallest integer greater than or equal to a number.
|
|
441
|
+
///
|
|
442
|
+
/// This method is only available when the `std` feature is enabled.
|
|
443
|
+
#[cfg(feature = "std")]
|
|
444
|
+
#[inline(always)]
|
|
445
|
+
pub fn ceil(self) -> Self {
|
|
446
|
+
Self::from_f32(self.to_f32().ceil())
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/// Returns the nearest integer to a number. Round half-way cases away from 0.0.
|
|
450
|
+
///
|
|
451
|
+
/// This method is only available when the `std` feature is enabled.
|
|
452
|
+
#[cfg(feature = "std")]
|
|
453
|
+
#[inline(always)]
|
|
454
|
+
pub fn round(self) -> Self {
|
|
455
|
+
Self::from_f32(self.to_f32().round())
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
#[cfg(feature = "std")]
|
|
460
|
+
impl std::fmt::Display for bf16 {
|
|
461
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
462
|
+
write!(f, "{}", self.to_f32())
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
impl core::ops::Add for bf16 {
|
|
467
|
+
type Output = Self;
|
|
468
|
+
|
|
469
|
+
#[inline(always)]
|
|
470
|
+
fn add(self, rhs: Self) -> Self::Output {
|
|
471
|
+
Self::from_f32(self.to_f32() + rhs.to_f32())
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
impl core::ops::Sub for bf16 {
|
|
476
|
+
type Output = Self;
|
|
477
|
+
|
|
478
|
+
#[inline(always)]
|
|
479
|
+
fn sub(self, rhs: Self) -> Self::Output {
|
|
480
|
+
Self::from_f32(self.to_f32() - rhs.to_f32())
|
|
481
|
+
}
|
|
482
|
+
}
|
|
150
483
|
|
|
151
|
-
impl bf16 {
|
|
484
|
+
impl core::ops::Mul for bf16 {
|
|
485
|
+
type Output = Self;
|
|
486
|
+
|
|
487
|
+
#[inline(always)]
|
|
488
|
+
fn mul(self, rhs: Self) -> Self::Output {
|
|
489
|
+
Self::from_f32(self.to_f32() * rhs.to_f32())
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
impl core::ops::Div for bf16 {
|
|
494
|
+
type Output = Self;
|
|
495
|
+
|
|
496
|
+
#[inline(always)]
|
|
497
|
+
fn div(self, rhs: Self) -> Self::Output {
|
|
498
|
+
Self::from_f32(self.to_f32() / rhs.to_f32())
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
impl core::ops::Neg for bf16 {
|
|
503
|
+
type Output = Self;
|
|
504
|
+
|
|
505
|
+
#[inline(always)]
|
|
506
|
+
fn neg(self) -> Self::Output {
|
|
507
|
+
Self::from_f32(-self.to_f32())
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
impl core::cmp::PartialOrd for bf16 {
|
|
512
|
+
#[inline(always)]
|
|
513
|
+
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
|
514
|
+
self.to_f32().partial_cmp(&other.to_f32())
|
|
515
|
+
}
|
|
516
|
+
}
|
|
152
517
|
|
|
153
518
|
/// The `capabilities` module provides functions for detecting the hardware features
|
|
154
519
|
/// available on the current system.
|
|
@@ -215,22 +580,22 @@ pub mod capabilities {
|
|
|
215
580
|
}
|
|
216
581
|
|
|
217
582
|
/// Flushes denormalized numbers to zero on the current CPU architecture.
|
|
218
|
-
///
|
|
583
|
+
///
|
|
219
584
|
/// This function should be called on each thread before any SIMD operations
|
|
220
|
-
/// to avoid performance penalties. When facing denormalized values,
|
|
585
|
+
/// to avoid performance penalties. When facing denormalized values,
|
|
221
586
|
/// Fused-Multiply-Add (FMA) operations can be up to 30x slower.
|
|
222
|
-
///
|
|
587
|
+
///
|
|
223
588
|
/// # Returns
|
|
224
|
-
///
|
|
589
|
+
///
|
|
225
590
|
/// Returns `true` if the operation was successful, `false` otherwise.
|
|
226
591
|
pub fn flush_denormals() -> bool {
|
|
227
592
|
unsafe { crate::simsimd_flush_denormals() != 0 }
|
|
228
593
|
}
|
|
229
594
|
|
|
230
595
|
/// Checks if the library is using dynamic dispatch for function selection.
|
|
231
|
-
///
|
|
596
|
+
///
|
|
232
597
|
/// # Returns
|
|
233
|
-
///
|
|
598
|
+
///
|
|
234
599
|
/// Returns `true` if dynamic dispatch is enabled, `false` otherwise.
|
|
235
600
|
/// Currently always returns `false` as dynamic dispatch is not implemented.
|
|
236
601
|
pub fn uses_dynamic_dispatch() -> bool {
|
|
@@ -1237,4 +1602,62 @@ mod tests {
|
|
|
1237
1602
|
}
|
|
1238
1603
|
}
|
|
1239
1604
|
}
|
|
1605
|
+
|
|
1606
|
+
#[test]
|
|
1607
|
+
fn test_f16_arithmetic() {
|
|
1608
|
+
let a = f16::from_f32(3.5);
|
|
1609
|
+
let b = f16::from_f32(2.0);
|
|
1610
|
+
|
|
1611
|
+
// Test basic arithmetic
|
|
1612
|
+
assert!((a + b).to_f32() - 5.5 < 0.01);
|
|
1613
|
+
assert!((a - b).to_f32() - 1.5 < 0.01);
|
|
1614
|
+
assert!((a * b).to_f32() - 7.0 < 0.01);
|
|
1615
|
+
assert!((a / b).to_f32() - 1.75 < 0.01);
|
|
1616
|
+
assert!((-a).to_f32() + 3.5 < 0.01);
|
|
1617
|
+
|
|
1618
|
+
// Test constants
|
|
1619
|
+
assert!(f16::ZERO.to_f32() == 0.0);
|
|
1620
|
+
assert!((f16::ONE.to_f32() - 1.0).abs() < 0.01);
|
|
1621
|
+
assert!((f16::NEG_ONE.to_f32() + 1.0).abs() < 0.01);
|
|
1622
|
+
|
|
1623
|
+
// Test comparisons
|
|
1624
|
+
assert!(a > b);
|
|
1625
|
+
assert!(!(a < b));
|
|
1626
|
+
assert!(a == a);
|
|
1627
|
+
|
|
1628
|
+
// Test utility methods
|
|
1629
|
+
assert!((-a).abs().to_f32() - 3.5 < 0.01);
|
|
1630
|
+
assert!(a.is_finite());
|
|
1631
|
+
assert!(!a.is_nan());
|
|
1632
|
+
assert!(!a.is_infinite());
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
#[test]
|
|
1636
|
+
fn test_bf16_arithmetic() {
|
|
1637
|
+
let a = bf16::from_f32(3.5);
|
|
1638
|
+
let b = bf16::from_f32(2.0);
|
|
1639
|
+
|
|
1640
|
+
// Test basic arithmetic
|
|
1641
|
+
assert!((a + b).to_f32() - 5.5 < 0.1);
|
|
1642
|
+
assert!((a - b).to_f32() - 1.5 < 0.1);
|
|
1643
|
+
assert!((a * b).to_f32() - 7.0 < 0.1);
|
|
1644
|
+
assert!((a / b).to_f32() - 1.75 < 0.1);
|
|
1645
|
+
assert!((-a).to_f32() + 3.5 < 0.1);
|
|
1646
|
+
|
|
1647
|
+
// Test constants
|
|
1648
|
+
assert!(bf16::ZERO.to_f32() == 0.0);
|
|
1649
|
+
assert!((bf16::ONE.to_f32() - 1.0).abs() < 0.01);
|
|
1650
|
+
assert!((bf16::NEG_ONE.to_f32() + 1.0).abs() < 0.01);
|
|
1651
|
+
|
|
1652
|
+
// Test comparisons
|
|
1653
|
+
assert!(a > b);
|
|
1654
|
+
assert!(!(a < b));
|
|
1655
|
+
assert!(a == a);
|
|
1656
|
+
|
|
1657
|
+
// Test utility methods
|
|
1658
|
+
assert!((-a).abs().to_f32() - 3.5 < 0.1);
|
|
1659
|
+
assert!(a.is_finite());
|
|
1660
|
+
assert!(!a.is_nan());
|
|
1661
|
+
assert!(!a.is_infinite());
|
|
1662
|
+
}
|
|
1240
1663
|
}
|