Skip to main content

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}