1use core::fmt;
16use core::simd::prelude::*;
17
18use crate::{Uf16, Uf16E5M11, Uf16E6M10, Uf32};
19
20#[cfg(any(target_feature = "avx2", target_feature = "avx512f"))]
22pub const UF16_LANES: usize = 8;
23#[cfg(not(any(target_feature = "avx2", target_feature = "avx512f")))]
25pub const UF16_LANES: usize = 4;
26
27#[cfg(any(target_feature = "avx2", target_feature = "avx512f"))]
29pub const UF32_LANES: usize = 4;
30#[cfg(not(any(target_feature = "avx2", target_feature = "avx512f")))]
32pub const UF32_LANES: usize = 2;
33
34#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36pub enum SimdError {
37 InputLengthMismatch { left: usize, right: usize },
39 OutputLengthMismatch { input: usize, output: usize },
41}
42
43impl fmt::Display for SimdError {
44 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
45 match self {
46 Self::InputLengthMismatch { left, right } => {
47 write!(formatter, "SIMD input lengths differ ({left} and {right})")
48 }
49 Self::OutputLengthMismatch { input, output } => {
50 write!(
51 formatter,
52 "SIMD output length is {output}, expected {input}"
53 )
54 }
55 }
56 }
57}
58
59trait Uf16Layout: Copy {
60 const EXPONENT_BITS: u32;
61 const MANTISSA_BITS: u32;
62 const F32_EXPONENT_BIAS: u32;
64
65 fn from_bits(bits: u16) -> Self;
66 fn to_bits(self) -> u16;
67 fn from_f32(value: f32) -> Self;
68 fn to_f32(self) -> f32;
69}
70
71impl Uf16Layout for Uf16E5M11 {
72 const EXPONENT_BITS: u32 = 5;
73 const MANTISSA_BITS: u32 = 11;
74 const F32_EXPONENT_BIAS: u32 = 112;
75
76 fn from_bits(bits: u16) -> Self {
77 Self::from_bits(bits)
78 }
79
80 fn to_bits(self) -> u16 {
81 self.to_bits()
82 }
83
84 fn from_f32(value: f32) -> Self {
85 Self::from_f32(value)
86 }
87
88 fn to_f32(self) -> f32 {
89 self.to_f32()
90 }
91}
92
93impl Uf16Layout for Uf16E6M10 {
94 const EXPONENT_BITS: u32 = 6;
95 const MANTISSA_BITS: u32 = 10;
96 const F32_EXPONENT_BIAS: u32 = 96;
97
98 fn from_bits(bits: u16) -> Self {
99 Self::from_bits(bits)
100 }
101
102 fn to_bits(self) -> u16 {
103 self.to_bits()
104 }
105
106 fn from_f32(value: f32) -> Self {
107 Self::from_f32(value)
108 }
109
110 fn to_f32(self) -> f32 {
111 self.to_f32()
112 }
113}
114
115fn output_len(input: usize, output: usize) -> Result<(), SimdError> {
116 if input == output {
117 Ok(())
118 } else {
119 Err(SimdError::OutputLengthMismatch { input, output })
120 }
121}
122
123fn binary_len(left: usize, right: usize, output: usize) -> Result<(), SimdError> {
124 if left != right {
125 return Err(SimdError::InputLengthMismatch { left, right });
126 }
127 output_len(left, output)
128}
129
130fn uf16_max_exponent<T: Uf16Layout>() -> u32 {
131 (1 << T::EXPONENT_BITS) - 1
132}
133
134fn can_decode_uf16<T: Uf16Layout>(value: T) -> bool {
135 let exponent = (value.to_bits() as u32 >> T::MANTISSA_BITS) & uf16_max_exponent::<T>();
136 exponent != 0 && exponent != uf16_max_exponent::<T>()
137}
138
139fn can_encode_uf16<T: Uf16Layout>(value: f32) -> bool {
140 let bits = value.to_bits();
141 let exponent = (bits >> 23) & 0xff;
142 let max_normal = uf16_max_exponent::<T>() - 1;
143 bits >> 31 == 0
144 && exponent > T::F32_EXPONENT_BIAS
145 && exponent < T::F32_EXPONENT_BIAS + max_normal
148}
149
150fn decode_uf16_fast<T: Uf16Layout>(src: &[T]) -> Simd<f32, UF16_LANES> {
151 debug_assert_eq!(src.len(), UF16_LANES);
152 debug_assert!(src.iter().copied().all(can_decode_uf16::<T>));
153 let raw = Simd::<u32, UF16_LANES>::from_array(core::array::from_fn(|lane| {
154 src[lane].to_bits() as u32
155 }));
156 let bits =
157 (raw << Simd::splat(23 - T::MANTISSA_BITS)) + Simd::splat(T::F32_EXPONENT_BIAS << 23);
158 Simd::<f32, UF16_LANES>::from_bits(bits)
159}
160
161fn encode_uf16_fast<T: Uf16Layout>(src: Simd<f32, UF16_LANES>, dst: &mut [T]) {
162 debug_assert_eq!(dst.len(), UF16_LANES);
163 debug_assert!(src.to_array().into_iter().all(can_encode_uf16::<T>));
164 let bits = src.to_bits();
165 let fraction = bits & Simd::splat(0x007f_ffff_u32);
166 let drop = 23 - T::MANTISSA_BITS;
167 let mantissa = fraction >> Simd::splat(drop);
168 let discarded = fraction & Simd::splat((1_u32 << drop) - 1);
169 let rounding =
171 (discarded + Simd::splat((1_u32 << (drop - 1)) - 1) + (mantissa & Simd::splat(1)))
172 >> Simd::splat(drop);
173 let rounded = mantissa + rounding;
174 let carry = rounded >> Simd::splat(T::MANTISSA_BITS);
175 let exponent = (bits >> Simd::splat(23)) - Simd::splat(T::F32_EXPONENT_BIAS) + carry;
176 let raw = (exponent << Simd::splat(T::MANTISSA_BITS))
177 | (rounded & Simd::splat((1_u32 << T::MANTISSA_BITS) - 1));
178 for (lane, bits) in raw.to_array().into_iter().enumerate() {
179 dst[lane] = T::from_bits(bits as u16);
180 }
181}
182
183fn decode_uf16<T: Uf16Layout>(src: &[T], dst: &mut [f32]) -> Result<(), SimdError> {
184 output_len(src.len(), dst.len())?;
185 let vector_end = src.len() / UF16_LANES * UF16_LANES;
186 for offset in (0..vector_end).step_by(UF16_LANES) {
187 let input = &src[offset..offset + UF16_LANES];
188 if input.iter().copied().all(can_decode_uf16::<T>) {
189 decode_uf16_fast(input).copy_to_slice(&mut dst[offset..]);
190 } else {
191 for lane in 0..UF16_LANES {
192 dst[offset + lane] = input[lane].to_f32();
193 }
194 }
195 }
196 for index in vector_end..src.len() {
197 dst[index] = src[index].to_f32();
198 }
199 Ok(())
200}
201
202fn encode_uf16<T: Uf16Layout>(src: &[f32], dst: &mut [T]) -> Result<(), SimdError> {
203 output_len(src.len(), dst.len())?;
204 let vector_end = src.len() / UF16_LANES * UF16_LANES;
205 for offset in (0..vector_end).step_by(UF16_LANES) {
206 let input = Simd::<f32, UF16_LANES>::from_slice(&src[offset..]);
207 if input.to_array().into_iter().all(can_encode_uf16::<T>) {
208 encode_uf16_fast(input, &mut dst[offset..offset + UF16_LANES]);
209 } else {
210 for lane in 0..UF16_LANES {
211 dst[offset + lane] = T::from_f32(src[offset + lane]);
212 }
213 }
214 }
215 for index in vector_end..src.len() {
216 dst[index] = T::from_f32(src[index]);
217 }
218 Ok(())
219}
220
221fn binary_uf16<T: Uf16Layout>(
222 left: &[T],
223 right: &[T],
224 output: &mut [T],
225 vector: impl Fn(Simd<f32, UF16_LANES>, Simd<f32, UF16_LANES>) -> Simd<f32, UF16_LANES>,
226 scalar: impl Fn(f32, f32) -> f32,
227) -> Result<(), SimdError> {
228 binary_len(left.len(), right.len(), output.len())?;
229 let vector_end = left.len() / UF16_LANES * UF16_LANES;
230 for offset in (0..vector_end).step_by(UF16_LANES) {
231 let lhs = &left[offset..offset + UF16_LANES];
232 let rhs = &right[offset..offset + UF16_LANES];
233 if lhs.iter().copied().all(can_decode_uf16::<T>)
234 && rhs.iter().copied().all(can_decode_uf16::<T>)
235 {
236 let result = vector(decode_uf16_fast(lhs), decode_uf16_fast(rhs));
237 if result.to_array().into_iter().all(can_encode_uf16::<T>) {
238 encode_uf16_fast(result, &mut output[offset..offset + UF16_LANES]);
239 continue;
240 }
241 }
242 for lane in 0..UF16_LANES {
243 output[offset + lane] = T::from_f32(scalar(lhs[lane].to_f32(), rhs[lane].to_f32()));
244 }
245 }
246 for index in vector_end..left.len() {
247 output[index] = T::from_f32(scalar(left[index].to_f32(), right[index].to_f32()));
248 }
249 Ok(())
250}
251
252pub fn decode_uf16_to_f32(src: &[Uf16], dst: &mut [f32]) -> Result<(), SimdError> {
254 decode_uf16(src, dst)
255}
256
257pub fn encode_f32_to_uf16(src: &[f32], dst: &mut [Uf16]) -> Result<(), SimdError> {
259 encode_uf16(src, dst)
260}
261
262pub fn decode_uf16e6m10_to_f32(src: &[Uf16E6M10], dst: &mut [f32]) -> Result<(), SimdError> {
264 decode_uf16(src, dst)
265}
266
267pub fn encode_f32_to_uf16e6m10(src: &[f32], dst: &mut [Uf16E6M10]) -> Result<(), SimdError> {
269 encode_uf16(src, dst)
270}
271
272pub fn add_uf16(left: &[Uf16], right: &[Uf16], output: &mut [Uf16]) -> Result<(), SimdError> {
274 binary_uf16(
275 left,
276 right,
277 output,
278 |left, right| left + right,
279 |left, right| left + right,
280 )
281}
282
283pub fn sub_uf16(left: &[Uf16], right: &[Uf16], output: &mut [Uf16]) -> Result<(), SimdError> {
285 binary_uf16(
286 left,
287 right,
288 output,
289 |left, right| left - right,
290 |left, right| left - right,
291 )
292}
293
294pub fn mul_uf16(left: &[Uf16], right: &[Uf16], output: &mut [Uf16]) -> Result<(), SimdError> {
296 binary_uf16(
297 left,
298 right,
299 output,
300 |left, right| left * right,
301 |left, right| left * right,
302 )
303}
304
305pub fn div_uf16(left: &[Uf16], right: &[Uf16], output: &mut [Uf16]) -> Result<(), SimdError> {
307 binary_uf16(
308 left,
309 right,
310 output,
311 |left, right| left / right,
312 |left, right| left / right,
313 )
314}
315
316pub fn add_uf16e6m10(
318 left: &[Uf16E6M10],
319 right: &[Uf16E6M10],
320 output: &mut [Uf16E6M10],
321) -> Result<(), SimdError> {
322 binary_uf16(
323 left,
324 right,
325 output,
326 |left, right| left + right,
327 |left, right| left + right,
328 )
329}
330
331pub fn sub_uf16e6m10(
333 left: &[Uf16E6M10],
334 right: &[Uf16E6M10],
335 output: &mut [Uf16E6M10],
336) -> Result<(), SimdError> {
337 binary_uf16(
338 left,
339 right,
340 output,
341 |left, right| left - right,
342 |left, right| left - right,
343 )
344}
345
346pub fn mul_uf16e6m10(
348 left: &[Uf16E6M10],
349 right: &[Uf16E6M10],
350 output: &mut [Uf16E6M10],
351) -> Result<(), SimdError> {
352 binary_uf16(
353 left,
354 right,
355 output,
356 |left, right| left * right,
357 |left, right| left * right,
358 )
359}
360
361pub fn div_uf16e6m10(
363 left: &[Uf16E6M10],
364 right: &[Uf16E6M10],
365 output: &mut [Uf16E6M10],
366) -> Result<(), SimdError> {
367 binary_uf16(
368 left,
369 right,
370 output,
371 |left, right| left / right,
372 |left, right| left / right,
373 )
374}
375
376fn can_decode_uf32(value: Uf32) -> bool {
377 let exponent = value.to_bits() >> 24;
378 exponent != 0 && exponent != 0xff
379}
380
381fn can_encode_uf32(value: f64) -> bool {
382 let bits = value.to_bits();
383 let exponent = (bits >> 52) & 0x7ff;
384 bits >> 63 == 0 && exponent > 896 && exponent < 1150
386}
387
388fn decode_uf32_fast(src: &[Uf32]) -> Simd<f64, UF32_LANES> {
389 debug_assert_eq!(src.len(), UF32_LANES);
390 debug_assert!(src.iter().copied().all(can_decode_uf32));
391 let raw = Simd::<u64, UF32_LANES>::from_array(core::array::from_fn(|lane| {
392 src[lane].to_bits() as u64
393 }));
394 let bits = (raw << Simd::splat(28)) + Simd::splat(896_u64 << 52);
395 Simd::<f64, UF32_LANES>::from_bits(bits)
396}
397
398fn encode_uf32_fast(src: Simd<f64, UF32_LANES>, dst: &mut [Uf32]) {
399 debug_assert_eq!(dst.len(), UF32_LANES);
400 debug_assert!(src.to_array().into_iter().all(can_encode_uf32));
401 let bits = src.to_bits();
402 let fraction = bits & Simd::splat(0x000f_ffff_ffff_ffff_u64);
403 let mantissa = fraction >> Simd::splat(28);
404 let discarded = fraction & Simd::splat((1_u64 << 28) - 1);
405 let rounding = (discarded + Simd::splat((1_u64 << 27) - 1) + (mantissa & Simd::splat(1)))
406 >> Simd::splat(28);
407 let rounded = mantissa + rounding;
408 let carry = rounded >> Simd::splat(24);
409 let exponent = (bits >> Simd::splat(52)) - Simd::splat(896_u64) + carry;
410 let raw = (exponent << Simd::splat(24)) | (rounded & Simd::splat(0x00ff_ffff_u64));
411 for (lane, bits) in raw.to_array().into_iter().enumerate() {
412 dst[lane] = Uf32::from_bits(bits as u32);
413 }
414}
415
416pub fn decode_uf32_to_f64(src: &[Uf32], dst: &mut [f64]) -> Result<(), SimdError> {
418 output_len(src.len(), dst.len())?;
419 let vector_end = src.len() / UF32_LANES * UF32_LANES;
420 for offset in (0..vector_end).step_by(UF32_LANES) {
421 let input = &src[offset..offset + UF32_LANES];
422 if input.iter().copied().all(can_decode_uf32) {
423 decode_uf32_fast(input).copy_to_slice(&mut dst[offset..]);
424 } else {
425 for lane in 0..UF32_LANES {
426 dst[offset + lane] = input[lane].to_f64();
427 }
428 }
429 }
430 for index in vector_end..src.len() {
431 dst[index] = src[index].to_f64();
432 }
433 Ok(())
434}
435
436pub fn encode_f64_to_uf32(src: &[f64], dst: &mut [Uf32]) -> Result<(), SimdError> {
438 output_len(src.len(), dst.len())?;
439 let vector_end = src.len() / UF32_LANES * UF32_LANES;
440 for offset in (0..vector_end).step_by(UF32_LANES) {
441 let input = Simd::<f64, UF32_LANES>::from_slice(&src[offset..]);
442 if input.to_array().into_iter().all(can_encode_uf32) {
443 encode_uf32_fast(input, &mut dst[offset..offset + UF32_LANES]);
444 } else {
445 for lane in 0..UF32_LANES {
446 dst[offset + lane] = Uf32::from_f64(src[offset + lane]);
447 }
448 }
449 }
450 for index in vector_end..src.len() {
451 dst[index] = Uf32::from_f64(src[index]);
452 }
453 Ok(())
454}
455
456fn binary_uf32(
457 left: &[Uf32],
458 right: &[Uf32],
459 output: &mut [Uf32],
460 vector: impl Fn(Simd<f64, UF32_LANES>, Simd<f64, UF32_LANES>) -> Simd<f64, UF32_LANES>,
461 scalar: impl Fn(f64, f64) -> f64,
462) -> Result<(), SimdError> {
463 binary_len(left.len(), right.len(), output.len())?;
464 let vector_end = left.len() / UF32_LANES * UF32_LANES;
465 for offset in (0..vector_end).step_by(UF32_LANES) {
466 let lhs = &left[offset..offset + UF32_LANES];
467 let rhs = &right[offset..offset + UF32_LANES];
468 if lhs.iter().copied().all(can_decode_uf32) && rhs.iter().copied().all(can_decode_uf32) {
469 let result = vector(decode_uf32_fast(lhs), decode_uf32_fast(rhs));
470 if result.to_array().into_iter().all(can_encode_uf32) {
471 encode_uf32_fast(result, &mut output[offset..offset + UF32_LANES]);
472 continue;
473 }
474 }
475 for lane in 0..UF32_LANES {
476 output[offset + lane] = Uf32::from_f64(scalar(lhs[lane].to_f64(), rhs[lane].to_f64()));
477 }
478 }
479 for index in vector_end..left.len() {
480 output[index] = Uf32::from_f64(scalar(left[index].to_f64(), right[index].to_f64()));
481 }
482 Ok(())
483}
484
485pub fn add_uf32(left: &[Uf32], right: &[Uf32], output: &mut [Uf32]) -> Result<(), SimdError> {
487 binary_uf32(
488 left,
489 right,
490 output,
491 |left, right| left + right,
492 |left, right| left + right,
493 )
494}
495
496pub fn sub_uf32(left: &[Uf32], right: &[Uf32], output: &mut [Uf32]) -> Result<(), SimdError> {
498 binary_uf32(
499 left,
500 right,
501 output,
502 |left, right| left - right,
503 |left, right| left - right,
504 )
505}
506
507pub fn mul_uf32(left: &[Uf32], right: &[Uf32], output: &mut [Uf32]) -> Result<(), SimdError> {
509 binary_uf32(
510 left,
511 right,
512 output,
513 |left, right| left * right,
514 |left, right| left * right,
515 )
516}
517
518pub fn div_uf32(left: &[Uf32], right: &[Uf32], output: &mut [Uf32]) -> Result<(), SimdError> {
520 binary_uf32(
521 left,
522 right,
523 output,
524 |left, right| left / right,
525 |left, right| left / right,
526 )
527}
528
529#[cfg(test)]
530mod tests {
531 use super::*;
532 use std::vec;
533 use std::vec::Vec;
534
535 fn lcg(state: &mut u64) -> u64 {
536 *state = state
537 .wrapping_mul(6_364_136_223_846_793_005)
538 .wrapping_add(1_442_695_040_888_963_407);
539 *state
540 }
541
542 fn verify_uf16_conversions<T: Uf16Layout>() {
543 let source: Vec<T> = (u16::MIN..=u16::MAX).map(T::from_bits).collect();
544 let mut decoded = vec![0.0; source.len()];
545 decode_uf16(&source, &mut decoded).unwrap();
546 for (value, actual) in source.iter().copied().zip(decoded) {
547 assert_eq!(actual.to_bits(), value.to_f32().to_bits());
548 }
549
550 let mut state = 0x6f_1d_5eed_u64;
551 let mut input = vec![0.0; 32_771];
552 input[0] = 0.0;
553 input[1] = -0.0;
554 input[2] = f32::INFINITY;
555 input[3] = f32::NEG_INFINITY;
556 input[4] = f32::NAN;
557 for value in &mut input[5..] {
558 *value = f32::from_bits(lcg(&mut state) as u32);
559 }
560 let mut encoded = vec![T::from_bits(0); input.len()];
561 encode_uf16(&input, &mut encoded).unwrap();
562 for (value, actual) in input.into_iter().zip(encoded) {
563 assert_eq!(actual.to_bits(), T::from_f32(value).to_bits());
564 }
565 }
566
567 fn verify_uf16_binary<T: Uf16Layout>() {
568 let mut state = 0x9a_6d_ef_41_u64;
569 let left: Vec<T> = (0..(UF16_LANES * 19 + 3))
570 .map(|_| T::from_bits(lcg(&mut state) as u16))
571 .collect();
572 let right: Vec<T> = (0..left.len())
573 .map(|_| T::from_bits(lcg(&mut state) as u16))
574 .collect();
575 let mut output = vec![T::from_bits(0); left.len()];
576
577 binary_uf16(
578 &left,
579 &right,
580 &mut output,
581 |left, right| left + right,
582 |left, right| left + right,
583 )
584 .unwrap();
585 for ((left, right), actual) in left.iter().zip(&right).zip(&output) {
586 assert_eq!(
587 actual.to_bits(),
588 T::from_f32(left.to_f32() + right.to_f32()).to_bits()
589 );
590 }
591
592 binary_uf16(
593 &left,
594 &right,
595 &mut output,
596 |left, right| left - right,
597 |left, right| left - right,
598 )
599 .unwrap();
600 for ((left, right), actual) in left.iter().zip(&right).zip(&output) {
601 assert_eq!(
602 actual.to_bits(),
603 T::from_f32(left.to_f32() - right.to_f32()).to_bits()
604 );
605 }
606
607 binary_uf16(
608 &left,
609 &right,
610 &mut output,
611 |left, right| left * right,
612 |left, right| left * right,
613 )
614 .unwrap();
615 for ((left, right), actual) in left.iter().zip(&right).zip(&output) {
616 assert_eq!(
617 actual.to_bits(),
618 T::from_f32(left.to_f32() * right.to_f32()).to_bits()
619 );
620 }
621
622 binary_uf16(
623 &left,
624 &right,
625 &mut output,
626 |left, right| left / right,
627 |left, right| left / right,
628 )
629 .unwrap();
630 for ((left, right), actual) in left.iter().zip(&right).zip(&output) {
631 assert_eq!(
632 actual.to_bits(),
633 T::from_f32(left.to_f32() / right.to_f32()).to_bits()
634 );
635 }
636 }
637
638 #[test]
639 fn uf16e5m11_bulk_paths_are_bit_exact() {
640 verify_uf16_conversions::<Uf16>();
641 verify_uf16_binary::<Uf16>();
642 }
643
644 #[test]
645 fn uf16e6m10_bulk_paths_are_bit_exact() {
646 verify_uf16_conversions::<Uf16E6M10>();
647 verify_uf16_binary::<Uf16E6M10>();
648 }
649
650 #[test]
651 fn uf32_bulk_paths_are_bit_exact() {
652 let mut state = 0x03_2d_99_ef_u64;
653 let mut source = vec![Uf32::ZERO, Uf32::MIN_POSITIVE, Uf32::INFINITY, Uf32::NAN];
654 source.extend((0..32_767).map(|_| Uf32::from_bits(lcg(&mut state) as u32)));
655 let mut decoded = vec![0.0; source.len()];
656 decode_uf32_to_f64(&source, &mut decoded).unwrap();
657 for (value, actual) in source.iter().copied().zip(decoded) {
658 assert_eq!(actual.to_bits(), value.to_f64().to_bits());
659 }
660
661 let mut encoded_input = vec![0.0; 32_771];
662 encoded_input[0] = 0.0;
663 encoded_input[1] = -0.0;
664 encoded_input[2] = f64::INFINITY;
665 encoded_input[3] = f64::NAN;
666 for value in &mut encoded_input[4..] {
667 *value = f64::from_bits(lcg(&mut state));
668 }
669 let mut encoded = vec![Uf32::ZERO; encoded_input.len()];
670 encode_f64_to_uf32(&encoded_input, &mut encoded).unwrap();
671 for (value, actual) in encoded_input.into_iter().zip(encoded) {
672 assert_eq!(actual.to_bits(), Uf32::from_f64(value).to_bits());
673 }
674
675 let left: Vec<Uf32> = (0..(UF32_LANES * 19 + 1))
676 .map(|_| Uf32::from_bits(lcg(&mut state) as u32))
677 .collect();
678 let right: Vec<Uf32> = (0..left.len())
679 .map(|_| Uf32::from_bits(lcg(&mut state) as u32))
680 .collect();
681 let mut output = vec![Uf32::ZERO; left.len()];
682 macro_rules! assert_uf32_binary {
683 ($vector:expr, $scalar:expr) => {{
684 binary_uf32(&left, &right, &mut output, $vector, $scalar).unwrap();
685 for ((left, right), actual) in left.iter().zip(&right).zip(&output) {
686 assert_eq!(
687 actual.to_bits(),
688 Uf32::from_f64($scalar(left.to_f64(), right.to_f64())).to_bits()
689 );
690 }
691 }};
692 }
693 assert_uf32_binary!(|left, right| left + right, |left: f64, right: f64| left
694 + right);
695 assert_uf32_binary!(|left, right| left - right, |left: f64, right: f64| left
696 - right);
697 assert_uf32_binary!(|left, right| left * right, |left: f64, right: f64| left
698 * right);
699 assert_uf32_binary!(|left, right| left / right, |left: f64, right: f64| left
700 / right);
701 }
702
703 #[test]
704 fn bulk_operations_reject_mismatched_planes() {
705 let input = [Uf16::ONE; 2];
706 let other = [Uf16::ONE; 1];
707 let mut output = [Uf16::ZERO; 2];
708 assert_eq!(
709 add_uf16(&input, &other, &mut output),
710 Err(SimdError::InputLengthMismatch { left: 2, right: 1 })
711 );
712 let mut short = [0.0; 1];
713 assert_eq!(
714 decode_uf16_to_f32(&input, &mut short),
715 Err(SimdError::OutputLengthMismatch {
716 input: 2,
717 output: 1
718 })
719 );
720 }
721}