qubit_atomic/atomic/atomic_f32.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10
11//! # Atomic 32-bit Floating Point
12//!
13//! Provides an easy-to-use atomic 32-bit floating point type with sensible
14//! default memory orderings. Implemented using bit conversion with AtomicU32.
15//!
16
17use std::sync::atomic::AtomicU32;
18use std::sync::atomic::Ordering;
19
20use crate::atomic::atomic_number_ops::AtomicNumberOps;
21use crate::atomic::atomic_ops::AtomicOps;
22
23/// Atomic 32-bit floating point number.
24///
25/// Provides easy-to-use atomic operations with automatic memory ordering
26/// selection. Implemented using `AtomicU32` with bit conversion.
27///
28/// # Memory Ordering Strategy
29///
30/// This type uses the same memory ordering strategy as atomic integers:
31///
32/// - **Read operations** (`load`): Use `Acquire` ordering to ensure
33/// visibility of prior writes from other threads.
34///
35/// - **Write operations** (`store`): Use `Release` ordering to ensure
36/// visibility of prior writes to other threads.
37///
38/// - **Read-Modify-Write operations** (`swap`, `compare_set`): Use
39/// `AcqRel` ordering for full synchronization.
40///
41/// - **CAS-based arithmetic** (`fetch_add`, `fetch_sub`, etc.): Use
42/// `AcqRel` on success and `Acquire` on failure within the CAS loop.
43/// The loop ensures eventual consistency.
44///
45/// # Implementation Details
46///
47/// Since hardware doesn't provide native atomic floating-point operations,
48/// this type is implemented using `AtomicU32` with `f32::to_bits()` and
49/// `f32::from_bits()` conversions. This preserves bit patterns exactly,
50/// including special values like NaN and infinity.
51///
52/// # Features
53///
54/// - Automatic memory ordering selection
55/// - Arithmetic operations via CAS loops
56/// - Zero-cost abstraction with inline methods
57/// - Access to underlying type via `inner()` for advanced use cases
58///
59/// # Limitations
60///
61/// - Arithmetic operations use CAS loops (slower than integer operations)
62/// - CAS comparisons use exact IEEE-754 bit patterns, so different NaN
63/// payloads and `0.0`/`-0.0` are treated as different values
64/// - No max/min operations (complex floating point semantics)
65///
66/// # Example
67///
68/// ```rust
69/// use qubit_atomic::Atomic;
70/// use std::sync::Arc;
71/// use std::thread;
72///
73/// let sum = Arc::new(Atomic::<f32>::new(0.0));
74/// let mut handles = vec![];
75///
76/// for _ in 0..10 {
77/// let sum = sum.clone();
78/// let handle = thread::spawn(move || {
79/// for _ in 0..100 {
80/// sum.fetch_add(0.1);
81/// }
82/// });
83/// handles.push(handle);
84/// }
85///
86/// for handle in handles {
87/// handle.join().unwrap();
88/// }
89///
90/// // Note: Due to floating point precision, result may not be exactly 100.0
91/// let result = sum.load();
92/// assert!((result - 100.0).abs() < 0.01);
93/// ```
94///
95#[repr(transparent)]
96pub struct AtomicF32 {
97 /// Raw-bit atomic storage for the `f32` value.
98 inner: AtomicU32,
99}
100
101impl AtomicF32 {
102 /// Creates a new atomic floating point number.
103 ///
104 /// # Parameters
105 ///
106 /// * `value` - The initial value.
107 ///
108 /// # Returns
109 ///
110 /// An atomic `f32` initialized to `value`.
111 ///
112 /// # Example
113 ///
114 /// ```rust
115 /// use qubit_atomic::Atomic;
116 ///
117 /// let atomic = Atomic::<f32>::new(3.14);
118 /// assert_eq!(atomic.load(), 3.14);
119 /// ```
120 #[inline]
121 pub const fn new(value: f32) -> Self {
122 Self {
123 inner: AtomicU32::new(value.to_bits()),
124 }
125 }
126
127 /// Gets the current value.
128 ///
129 /// # Memory Ordering
130 ///
131 /// Uses `Acquire` ordering on the underlying `AtomicU32`. This ensures
132 /// that all writes from other threads that happened before a `Release`
133 /// store are visible after this load.
134 ///
135 /// # Returns
136 ///
137 /// The current value.
138 ///
139 /// # Example
140 ///
141 /// ```rust
142 /// use qubit_atomic::Atomic;
143 ///
144 /// let atomic = Atomic::<f32>::new(3.14);
145 /// assert_eq!(atomic.load(), 3.14);
146 /// ```
147 #[inline]
148 pub fn load(&self) -> f32 {
149 f32::from_bits(self.inner.load(Ordering::Acquire))
150 }
151
152 /// Sets a new value.
153 ///
154 /// # Memory Ordering
155 ///
156 /// Uses `Release` ordering on the underlying `AtomicU32`. This ensures
157 /// that all prior writes in this thread are visible to other threads
158 /// that perform an `Acquire` load.
159 ///
160 /// # Parameters
161 ///
162 /// * `value` - The new value to set.
163 ///
164 /// # Example
165 ///
166 /// ```rust
167 /// use qubit_atomic::Atomic;
168 ///
169 /// let atomic = Atomic::<f32>::new(0.0);
170 /// atomic.store(3.14);
171 /// assert_eq!(atomic.load(), 3.14);
172 /// ```
173 #[inline]
174 pub fn store(&self, value: f32) {
175 self.inner.store(value.to_bits(), Ordering::Release);
176 }
177
178 /// Swaps the current value with a new value, returning the old value.
179 ///
180 /// # Memory Ordering
181 ///
182 /// Uses `AcqRel` ordering on the underlying `AtomicU32`. This provides
183 /// full synchronization for this read-modify-write operation.
184 ///
185 /// # Parameters
186 ///
187 /// * `value` - The new value to swap in.
188 ///
189 /// # Returns
190 ///
191 /// The old value.
192 ///
193 /// # Example
194 ///
195 /// ```rust
196 /// use qubit_atomic::Atomic;
197 ///
198 /// let atomic = Atomic::<f32>::new(1.0);
199 /// let old = atomic.swap(2.0);
200 /// assert_eq!(old, 1.0);
201 /// assert_eq!(atomic.load(), 2.0);
202 /// ```
203 #[inline]
204 pub fn swap(&self, value: f32) -> f32 {
205 f32::from_bits(self.inner.swap(value.to_bits(), Ordering::AcqRel))
206 }
207
208 /// Compares and sets the value atomically.
209 ///
210 /// If the current value equals `current`, sets it to `new` and returns
211 /// `Ok(())`. Otherwise, returns `Err(actual)` where `actual` is the
212 /// current value.
213 ///
214 /// Comparison uses the exact raw bit pattern produced by
215 /// [`f32::to_bits`], not [`PartialEq`].
216 ///
217 /// # Memory Ordering
218 ///
219 /// - **Success**: Uses `AcqRel` ordering on the underlying `AtomicU32`
220 /// to ensure full synchronization when the exchange succeeds.
221 /// - **Failure**: Uses `Acquire` ordering to observe the actual value
222 /// written by another thread.
223 ///
224 /// # Parameters
225 ///
226 /// * `current` - The expected current value.
227 /// * `new` - The new value to set if current matches.
228 ///
229 /// # Returns
230 ///
231 /// `Ok(())` when the value was replaced.
232 ///
233 /// # Errors
234 ///
235 /// Returns `Err(actual)` with the observed value when the raw-bit
236 /// comparison fails. In that case, `new` is not stored.
237 ///
238 /// # Warning
239 ///
240 /// NaN values compare by raw bits. A stored NaN and `current` must have
241 /// the same payload bits for the CAS to succeed.
242 ///
243 /// # Example
244 ///
245 /// ```rust
246 /// use qubit_atomic::Atomic;
247 ///
248 /// let atomic = Atomic::<f32>::new(1.0);
249 /// assert!(atomic.compare_set(1.0, 2.0).is_ok());
250 /// assert_eq!(atomic.load(), 2.0);
251 /// ```
252 #[inline]
253 pub fn compare_set(&self, current: f32, new: f32) -> Result<(), f32> {
254 self.inner
255 .compare_exchange(
256 current.to_bits(),
257 new.to_bits(),
258 Ordering::AcqRel,
259 Ordering::Acquire,
260 )
261 .map(|_| ())
262 .map_err(f32::from_bits)
263 }
264
265 /// Weak version of compare-and-set.
266 ///
267 /// May spuriously fail even when the comparison succeeds. Should be used
268 /// in a loop.
269 ///
270 /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
271 /// Comparison uses the exact raw bit pattern produced by
272 /// [`f32::to_bits`].
273 ///
274 /// # Parameters
275 ///
276 /// * `current` - The expected current value.
277 /// * `new` - The new value to set if current matches.
278 ///
279 /// # Returns
280 ///
281 /// `Ok(())` when the value was replaced.
282 ///
283 /// # Errors
284 ///
285 /// Returns `Err(actual)` with the observed value when the raw-bit
286 /// comparison fails, including possible spurious failures. In that case,
287 /// `new` is not stored.
288 ///
289 /// # Example
290 ///
291 /// ```rust
292 /// use qubit_atomic::Atomic;
293 ///
294 /// let atomic = Atomic::<f32>::new(1.0);
295 /// let mut current = atomic.load();
296 /// loop {
297 /// match atomic.compare_set_weak(current, current + 1.0) {
298 /// Ok(_) => break,
299 /// Err(actual) => current = actual,
300 /// }
301 /// }
302 /// assert_eq!(atomic.load(), 2.0);
303 /// ```
304 #[inline]
305 pub fn compare_set_weak(&self, current: f32, new: f32) -> Result<(), f32> {
306 self.inner
307 .compare_exchange_weak(
308 current.to_bits(),
309 new.to_bits(),
310 Ordering::AcqRel,
311 Ordering::Acquire,
312 )
313 .map(|_| ())
314 .map_err(f32::from_bits)
315 }
316
317 /// Compares and exchanges the value atomically, returning the previous
318 /// value.
319 ///
320 /// If the current value equals `current`, sets it to `new` and returns
321 /// the old value. Otherwise, returns the actual current value.
322 ///
323 /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
324 ///
325 /// # Parameters
326 ///
327 /// * `current` - The expected current value.
328 /// * `new` - The new value to set if current matches.
329 ///
330 /// # Returns
331 ///
332 /// The value observed before the operation completed. If the returned
333 /// value has the same raw bits as `current`, the exchange succeeded;
334 /// otherwise it is the actual value that prevented the exchange.
335 ///
336 /// # Example
337 ///
338 /// ```rust
339 /// use qubit_atomic::Atomic;
340 ///
341 /// let atomic = Atomic::<f32>::new(1.0);
342 /// let prev = atomic.compare_and_exchange(1.0, 2.0);
343 /// assert_eq!(prev, 1.0);
344 /// assert_eq!(atomic.load(), 2.0);
345 /// ```
346 #[inline]
347 pub fn compare_and_exchange(&self, current: f32, new: f32) -> f32 {
348 match self.inner.compare_exchange(
349 current.to_bits(),
350 new.to_bits(),
351 Ordering::AcqRel,
352 Ordering::Acquire,
353 ) {
354 Ok(prev_bits) => f32::from_bits(prev_bits),
355 Err(actual_bits) => f32::from_bits(actual_bits),
356 }
357 }
358
359 /// Weak version of compare-and-exchange.
360 ///
361 /// May spuriously fail even when the comparison succeeds. Should be used
362 /// in a loop.
363 ///
364 /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
365 ///
366 /// # Parameters
367 ///
368 /// * `current` - The expected current value.
369 /// * `new` - The new value to set if current matches.
370 ///
371 /// # Returns
372 ///
373 /// The value observed before the operation completed. Because this
374 /// operation may fail spuriously, a returned value with the same raw bits
375 /// as `current` does not by itself prove that `new` was stored; use
376 /// [`compare_set_weak`](Self::compare_set_weak) when the caller needs an
377 /// explicit success indicator.
378 ///
379 /// # Example
380 ///
381 /// ```rust
382 /// use qubit_atomic::Atomic;
383 ///
384 /// let atomic = Atomic::<f32>::new(1.0);
385 /// let mut current = atomic.load();
386 /// loop {
387 /// let prev = atomic.compare_and_exchange_weak(current, current + 1.0);
388 /// if atomic.load() == 2.0 {
389 /// break;
390 /// }
391 /// current = prev;
392 /// }
393 /// assert_eq!(atomic.load(), 2.0);
394 /// ```
395 #[inline]
396 pub fn compare_and_exchange_weak(&self, current: f32, new: f32) -> f32 {
397 match self.inner.compare_exchange_weak(
398 current.to_bits(),
399 new.to_bits(),
400 Ordering::AcqRel,
401 Ordering::Acquire,
402 ) {
403 Ok(prev_bits) => f32::from_bits(prev_bits),
404 Err(actual_bits) => f32::from_bits(actual_bits),
405 }
406 }
407
408 /// Atomically adds a value, returning the old value.
409 ///
410 /// # Memory Ordering
411 ///
412 /// Internally uses a CAS loop with `compare_set_weak`, which uses
413 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
414 /// eventual consistency even under high contention.
415 ///
416 /// # Performance
417 ///
418 /// May be slow in high-contention scenarios due to the CAS loop.
419 /// Consider using atomic integers if performance is critical.
420 ///
421 /// # Parameters
422 ///
423 /// * `delta` - The value to add.
424 ///
425 /// # Returns
426 ///
427 /// The old value before adding.
428 ///
429 /// # Example
430 ///
431 /// ```rust
432 /// use qubit_atomic::Atomic;
433 ///
434 /// let atomic = Atomic::<f32>::new(10.0);
435 /// let old = atomic.fetch_add(5.5);
436 /// assert_eq!(old, 10.0);
437 /// assert_eq!(atomic.load(), 15.5);
438 /// ```
439 #[inline]
440 pub fn fetch_add(&self, delta: f32) -> f32 {
441 self.fetch_update(|current| current + delta)
442 }
443
444 /// Atomically subtracts a value, returning the old value.
445 ///
446 /// # Memory Ordering
447 ///
448 /// Internally uses a CAS loop with `compare_set_weak`, which uses
449 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
450 /// eventual consistency even under high contention.
451 ///
452 /// # Parameters
453 ///
454 /// * `delta` - The value to subtract.
455 ///
456 /// # Returns
457 ///
458 /// The old value before subtracting.
459 ///
460 /// # Example
461 ///
462 /// ```rust
463 /// use qubit_atomic::Atomic;
464 ///
465 /// let atomic = Atomic::<f32>::new(10.0);
466 /// let old = atomic.fetch_sub(3.5);
467 /// assert_eq!(old, 10.0);
468 /// assert_eq!(atomic.load(), 6.5);
469 /// ```
470 #[inline]
471 pub fn fetch_sub(&self, delta: f32) -> f32 {
472 self.fetch_update(|current| current - delta)
473 }
474
475 /// Atomically multiplies by a factor, returning the old value.
476 ///
477 /// # Memory Ordering
478 ///
479 /// Internally uses a CAS loop with `compare_set_weak`, which uses
480 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
481 /// eventual consistency even under high contention.
482 ///
483 /// # Parameters
484 ///
485 /// * `factor` - The factor to multiply by.
486 ///
487 /// # Returns
488 ///
489 /// The old value before multiplying.
490 ///
491 /// # Example
492 ///
493 /// ```rust
494 /// use qubit_atomic::Atomic;
495 ///
496 /// let atomic = Atomic::<f32>::new(10.0);
497 /// let old = atomic.fetch_mul(2.5);
498 /// assert_eq!(old, 10.0);
499 /// assert_eq!(atomic.load(), 25.0);
500 /// ```
501 #[inline]
502 pub fn fetch_mul(&self, factor: f32) -> f32 {
503 self.fetch_update(|current| current * factor)
504 }
505
506 /// Atomically divides by a divisor, returning the old value.
507 ///
508 /// # Memory Ordering
509 ///
510 /// Internally uses a CAS loop with `compare_set_weak`, which uses
511 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
512 /// eventual consistency even under high contention.
513 ///
514 /// # Parameters
515 ///
516 /// * `divisor` - The divisor to divide by.
517 ///
518 /// # Returns
519 ///
520 /// The old value before dividing.
521 ///
522 /// # Example
523 ///
524 /// ```rust
525 /// use qubit_atomic::Atomic;
526 ///
527 /// let atomic = Atomic::<f32>::new(10.0);
528 /// let old = atomic.fetch_div(2.0);
529 /// assert_eq!(old, 10.0);
530 /// assert_eq!(atomic.load(), 5.0);
531 /// ```
532 #[inline]
533 pub fn fetch_div(&self, divisor: f32) -> f32 {
534 self.fetch_update(|current| current / divisor)
535 }
536
537 /// Updates the value using a function, returning the old value.
538 ///
539 /// # Memory Ordering
540 ///
541 /// Internally uses a CAS loop with `compare_set_weak`, which uses
542 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
543 /// eventual consistency even under high contention.
544 ///
545 /// # Parameters
546 ///
547 /// * `f` - A function that takes the current value and returns the new
548 /// value.
549 ///
550 /// # Returns
551 ///
552 /// The old value before the update.
553 ///
554 /// The closure may be called more than once when concurrent updates cause
555 /// CAS retries.
556 ///
557 /// # Example
558 ///
559 /// ```rust
560 /// use qubit_atomic::Atomic;
561 ///
562 /// let atomic = Atomic::<f32>::new(10.0);
563 /// let old = atomic.fetch_update(|x| x * 2.0);
564 /// assert_eq!(old, 10.0);
565 /// assert_eq!(atomic.load(), 20.0);
566 /// ```
567 #[inline]
568 pub fn fetch_update<F>(&self, f: F) -> f32
569 where
570 F: Fn(f32) -> f32,
571 {
572 let mut current = self.load();
573 loop {
574 let new = f(current);
575 match self.compare_set_weak(current, new) {
576 Ok(_) => return current,
577 Err(actual) => current = actual,
578 }
579 }
580 }
581
582 /// Conditionally updates the value using a function.
583 ///
584 /// Internally uses a CAS loop until the update succeeds or the closure
585 /// rejects the current value by returning `None`.
586 ///
587 /// # Parameters
588 ///
589 /// * `f` - A function that takes the current value and returns the new
590 /// value, or `None` to leave the value unchanged.
591 ///
592 /// # Returns
593 ///
594 /// `Some(old_value)` when the update succeeds, or `None` when `f` rejects
595 /// the observed current value.
596 ///
597 /// The closure may be called more than once when concurrent updates cause
598 /// CAS retries.
599 ///
600 /// # Example
601 ///
602 /// ```rust
603 /// use qubit_atomic::Atomic;
604 ///
605 /// let atomic = Atomic::<f32>::new(1.5);
606 /// assert_eq!(atomic.try_update(|x| (x > 0.0).then_some(x * 2.0)), Some(1.5));
607 /// assert_eq!(atomic.load(), 3.0);
608 /// assert_eq!(atomic.try_update(|x| (x < 0.0).then_some(x * 2.0)), None);
609 /// assert_eq!(atomic.load(), 3.0);
610 /// ```
611 #[inline]
612 pub fn try_update<F>(&self, f: F) -> Option<f32>
613 where
614 F: Fn(f32) -> Option<f32>,
615 {
616 let mut current = self.load();
617 loop {
618 let new = f(current)?;
619 match self.compare_set_weak(current, new) {
620 Ok(_) => return Some(current),
621 Err(actual) => current = actual,
622 }
623 }
624 }
625
626 /// Gets a reference to the underlying standard library atomic type.
627 ///
628 /// This allows direct access to the standard library's atomic operations
629 /// for advanced use cases that require fine-grained control over memory
630 /// ordering.
631 ///
632 /// # Memory Ordering
633 ///
634 /// When using the returned reference, you have full control over memory
635 /// ordering. Remember to use `f32::to_bits()` and `f32::from_bits()` for
636 /// conversions.
637 ///
638 /// # Returns
639 ///
640 /// A reference to the underlying `std::sync::atomic::AtomicU32`.
641 ///
642 /// # Example
643 ///
644 /// ```rust
645 /// use qubit_atomic::Atomic;
646 /// use std::sync::atomic::Ordering;
647 ///
648 /// let atomic = Atomic::<f32>::new(0.0);
649 /// atomic.inner().store(3.14_f32.to_bits(), Ordering::Relaxed);
650 /// let bits = atomic.inner().load(Ordering::Relaxed);
651 /// assert_eq!(f32::from_bits(bits), 3.14);
652 /// ```
653 #[inline]
654 pub fn inner(&self) -> &AtomicU32 {
655 &self.inner
656 }
657}
658
659impl AtomicOps for AtomicF32 {
660 type Value = f32;
661
662 #[inline]
663 fn load(&self) -> f32 {
664 self.load()
665 }
666
667 #[inline]
668 fn store(&self, value: f32) {
669 self.store(value);
670 }
671
672 #[inline]
673 fn swap(&self, value: f32) -> f32 {
674 self.swap(value)
675 }
676
677 #[inline]
678 fn compare_set(&self, current: f32, new: f32) -> Result<(), f32> {
679 self.compare_set(current, new)
680 }
681
682 #[inline]
683 fn compare_set_weak(&self, current: f32, new: f32) -> Result<(), f32> {
684 self.compare_set_weak(current, new)
685 }
686
687 #[inline]
688 fn compare_exchange(&self, current: f32, new: f32) -> f32 {
689 self.compare_and_exchange(current, new)
690 }
691
692 #[inline]
693 fn compare_exchange_weak(&self, current: f32, new: f32) -> f32 {
694 self.compare_and_exchange_weak(current, new)
695 }
696
697 #[inline]
698 fn fetch_update<F>(&self, f: F) -> f32
699 where
700 F: Fn(f32) -> f32,
701 {
702 self.fetch_update(f)
703 }
704
705 #[inline]
706 fn try_update<F>(&self, f: F) -> Option<f32>
707 where
708 F: Fn(f32) -> Option<f32>,
709 {
710 self.try_update(f)
711 }
712}
713
714impl AtomicNumberOps for AtomicF32 {
715 #[inline]
716 fn fetch_add(&self, delta: f32) -> f32 {
717 self.fetch_add(delta)
718 }
719
720 #[inline]
721 fn fetch_sub(&self, delta: f32) -> f32 {
722 self.fetch_sub(delta)
723 }
724
725 #[inline]
726 fn fetch_mul(&self, factor: f32) -> f32 {
727 self.fetch_mul(factor)
728 }
729
730 #[inline]
731 fn fetch_div(&self, divisor: f32) -> f32 {
732 self.fetch_div(divisor)
733 }
734}