Skip to main content

qubit_atomic/atomic/
atomic_f64.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 64-bit Floating Point
12//!
13//! Provides an easy-to-use atomic 64-bit floating point type with sensible
14//! default memory orderings. Implemented using bit conversion with AtomicU64.
15//!
16
17use std::sync::atomic::AtomicU64;
18use std::sync::atomic::Ordering;
19
20use crate::atomic::atomic_number_ops::AtomicNumberOps;
21use crate::atomic::atomic_ops::AtomicOps;
22
23/// Atomic 64-bit floating point number.
24///
25/// Provides easy-to-use atomic operations with automatic memory ordering
26/// selection. Implemented using `AtomicU64` 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 `AtomicU64` with `f64::to_bits()` and
49/// `f64::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///
71/// let atomic = Atomic::<f64>::new(3.14159);
72/// atomic.fetch_add(1.0);
73/// assert_eq!(atomic.load(), 4.14159);
74/// ```
75///
76#[repr(transparent)]
77pub struct AtomicF64 {
78    /// Raw-bit atomic storage for the `f64` value.
79    inner: AtomicU64,
80}
81
82impl AtomicF64 {
83    /// Creates a new atomic floating point number.
84    ///
85    /// # Parameters
86    ///
87    /// * `value` - The initial value.
88    ///
89    /// # Returns
90    ///
91    /// An atomic `f64` initialized to `value`.
92    ///
93    /// # Example
94    ///
95    /// ```rust
96    /// use qubit_atomic::Atomic;
97    ///
98    /// let atomic = Atomic::<f64>::new(3.14159);
99    /// assert_eq!(atomic.load(), 3.14159);
100    /// ```
101    #[inline]
102    pub const fn new(value: f64) -> Self {
103        Self {
104            inner: AtomicU64::new(value.to_bits()),
105        }
106    }
107
108    /// Gets the current value.
109    ///
110    /// # Memory Ordering
111    ///
112    /// Uses `Acquire` ordering on the underlying `AtomicU64`. This ensures
113    /// that all writes from other threads that happened before a `Release`
114    /// store are visible after this load.
115    ///
116    /// # Returns
117    ///
118    /// The current value.
119    #[inline]
120    pub fn load(&self) -> f64 {
121        f64::from_bits(self.inner.load(Ordering::Acquire))
122    }
123
124    /// Sets a new value.
125    ///
126    /// # Memory Ordering
127    ///
128    /// Uses `Release` ordering on the underlying `AtomicU64`. This ensures
129    /// that all prior writes in this thread are visible to other threads
130    /// that perform an `Acquire` load.
131    ///
132    /// # Parameters
133    ///
134    /// * `value` - The new value to set.
135    #[inline]
136    pub fn store(&self, value: f64) {
137        self.inner.store(value.to_bits(), Ordering::Release);
138    }
139
140    /// Swaps the current value with a new value, returning the old value.
141    ///
142    /// # Memory Ordering
143    ///
144    /// Uses `AcqRel` ordering on the underlying `AtomicU64`. This provides
145    /// full synchronization for this read-modify-write operation.
146    ///
147    /// # Parameters
148    ///
149    /// * `value` - The new value to swap in.
150    ///
151    /// # Returns
152    ///
153    /// The old value.
154    #[inline]
155    pub fn swap(&self, value: f64) -> f64 {
156        f64::from_bits(self.inner.swap(value.to_bits(), Ordering::AcqRel))
157    }
158
159    /// Compares and sets the value atomically.
160    ///
161    /// If the current value equals `current`, sets it to `new` and returns
162    /// `Ok(())`. Otherwise, returns `Err(actual)` where `actual` is the
163    /// current value.
164    ///
165    /// Comparison uses the exact raw bit pattern produced by
166    /// [`f64::to_bits`], not [`PartialEq`].
167    ///
168    /// # Memory Ordering
169    ///
170    /// - **Success**: Uses `AcqRel` ordering on the underlying `AtomicU64`
171    ///   to ensure full synchronization when the exchange succeeds.
172    /// - **Failure**: Uses `Acquire` ordering to observe the actual value
173    ///   written by another thread.
174    ///
175    /// # Parameters
176    ///
177    /// * `current` - The expected current value.
178    /// * `new` - The new value to set if current matches.
179    ///
180    /// # Returns
181    ///
182    /// `Ok(())` when the value was replaced.
183    ///
184    /// # Errors
185    ///
186    /// Returns `Err(actual)` with the observed value when the raw-bit
187    /// comparison fails. In that case, `new` is not stored.
188    #[inline]
189    pub fn compare_set(&self, current: f64, new: f64) -> Result<(), f64> {
190        self.inner
191            .compare_exchange(current.to_bits(), new.to_bits(), Ordering::AcqRel, Ordering::Acquire)
192            .map(|_| ())
193            .map_err(f64::from_bits)
194    }
195
196    /// Weak version of compare-and-set.
197    ///
198    /// May spuriously fail even when the comparison succeeds. Should be used
199    /// in a loop.
200    ///
201    /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
202    /// Comparison uses the exact raw bit pattern produced by
203    /// [`f64::to_bits`].
204    ///
205    /// # Parameters
206    ///
207    /// * `current` - The expected current value.
208    /// * `new` - The new value to set if current matches.
209    ///
210    /// # Returns
211    ///
212    /// `Ok(())` when the value was replaced.
213    ///
214    /// # Errors
215    ///
216    /// Returns `Err(actual)` with the observed value when the raw-bit
217    /// comparison fails, including possible spurious failures. In that case,
218    /// `new` is not stored.
219    #[inline]
220    pub fn compare_set_weak(&self, current: f64, new: f64) -> Result<(), f64> {
221        self.inner
222            .compare_exchange_weak(current.to_bits(), new.to_bits(), Ordering::AcqRel, Ordering::Acquire)
223            .map(|_| ())
224            .map_err(f64::from_bits)
225    }
226
227    /// Compares and exchanges the value atomically, returning the previous
228    /// value.
229    ///
230    /// If the current value equals `current`, sets it to `new` and returns
231    /// the old value. Otherwise, returns the actual current value.
232    ///
233    /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
234    ///
235    /// # Parameters
236    ///
237    /// * `current` - The expected current value.
238    /// * `new` - The new value to set if current matches.
239    ///
240    /// # Returns
241    ///
242    /// The value observed before the operation completed. If the returned
243    /// value has the same raw bits as `current`, the exchange succeeded;
244    /// otherwise it is the actual value that prevented the exchange.
245    #[inline]
246    pub fn compare_and_exchange(&self, current: f64, new: f64) -> f64 {
247        match self
248            .inner
249            .compare_exchange(current.to_bits(), new.to_bits(), Ordering::AcqRel, Ordering::Acquire)
250        {
251            Ok(prev_bits) => f64::from_bits(prev_bits),
252            Err(actual_bits) => f64::from_bits(actual_bits),
253        }
254    }
255
256    /// Weak version of compare-and-exchange.
257    ///
258    /// May spuriously fail even when the comparison succeeds. Should be used
259    /// in a loop.
260    ///
261    /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
262    ///
263    /// # Parameters
264    ///
265    /// * `current` - The expected current value.
266    /// * `new` - The new value to set if current matches.
267    ///
268    /// # Returns
269    ///
270    /// `Ok(previous)` when the value was replaced, or `Err(actual)` when the
271    /// comparison failed, including possible spurious failure. Values preserve
272    /// their exact raw bit patterns.
273    #[inline]
274    pub fn compare_and_exchange_weak(&self, current: f64, new: f64) -> Result<f64, f64> {
275        self.inner
276            .compare_exchange_weak(current.to_bits(), new.to_bits(), Ordering::AcqRel, Ordering::Acquire)
277            .map(f64::from_bits)
278            .map_err(f64::from_bits)
279    }
280
281    /// Atomically adds a value, returning the old value.
282    ///
283    /// # Memory Ordering
284    ///
285    /// Internally uses a CAS loop with `compare_set_weak`, which uses
286    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
287    /// eventual consistency even under high contention.
288    ///
289    /// # Performance
290    ///
291    /// May be slow in high-contention scenarios due to the CAS loop.
292    /// Consider using atomic integers if performance is critical.
293    ///
294    /// # Parameters
295    ///
296    /// * `delta` - The value to add.
297    ///
298    /// # Returns
299    ///
300    /// The old value before adding.
301    ///
302    /// # Example
303    ///
304    /// ```rust
305    /// use qubit_atomic::Atomic;
306    ///
307    /// let atomic = Atomic::<f64>::new(10.0);
308    /// let old = atomic.fetch_add(5.5);
309    /// assert_eq!(old, 10.0);
310    /// assert_eq!(atomic.load(), 15.5);
311    /// ```
312    #[inline]
313    pub fn fetch_add(&self, delta: f64) -> f64 {
314        self.fetch_update(|current| current + delta)
315    }
316
317    /// Atomically subtracts a value, returning the old value.
318    ///
319    /// # Memory Ordering
320    ///
321    /// Internally uses a CAS loop with `compare_set_weak`, which uses
322    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
323    /// eventual consistency even under high contention.
324    ///
325    /// # Parameters
326    ///
327    /// * `delta` - The value to subtract.
328    ///
329    /// # Returns
330    ///
331    /// The old value before subtracting.
332    ///
333    /// # Example
334    ///
335    /// ```rust
336    /// use qubit_atomic::Atomic;
337    ///
338    /// let atomic = Atomic::<f64>::new(10.0);
339    /// let old = atomic.fetch_sub(3.5);
340    /// assert_eq!(old, 10.0);
341    /// assert_eq!(atomic.load(), 6.5);
342    /// ```
343    #[inline]
344    pub fn fetch_sub(&self, delta: f64) -> f64 {
345        self.fetch_update(|current| current - delta)
346    }
347
348    /// Atomically multiplies by a factor, returning the old value.
349    ///
350    /// # Memory Ordering
351    ///
352    /// Internally uses a CAS loop with `compare_set_weak`, which uses
353    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
354    /// eventual consistency even under high contention.
355    ///
356    /// # Parameters
357    ///
358    /// * `factor` - The factor to multiply by.
359    ///
360    /// # Returns
361    ///
362    /// The old value before multiplying.
363    ///
364    /// # Example
365    ///
366    /// ```rust
367    /// use qubit_atomic::Atomic;
368    ///
369    /// let atomic = Atomic::<f64>::new(10.0);
370    /// let old = atomic.fetch_mul(2.5);
371    /// assert_eq!(old, 10.0);
372    /// assert_eq!(atomic.load(), 25.0);
373    /// ```
374    #[inline]
375    pub fn fetch_mul(&self, factor: f64) -> f64 {
376        self.fetch_update(|current| current * factor)
377    }
378
379    /// Atomically divides by a divisor, returning the old value.
380    ///
381    /// # Memory Ordering
382    ///
383    /// Internally uses a CAS loop with `compare_set_weak`, which uses
384    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
385    /// eventual consistency even under high contention.
386    ///
387    /// # Parameters
388    ///
389    /// * `divisor` - The divisor to divide by.
390    ///
391    /// # Returns
392    ///
393    /// The old value before dividing.
394    ///
395    /// # Example
396    ///
397    /// ```rust
398    /// use qubit_atomic::Atomic;
399    ///
400    /// let atomic = Atomic::<f64>::new(10.0);
401    /// let old = atomic.fetch_div(2.0);
402    /// assert_eq!(old, 10.0);
403    /// assert_eq!(atomic.load(), 5.0);
404    /// ```
405    #[inline]
406    pub fn fetch_div(&self, divisor: f64) -> f64 {
407        self.fetch_update(|current| current / divisor)
408    }
409
410    /// Updates the value using a function, returning the old value.
411    ///
412    /// # Memory Ordering
413    ///
414    /// Internally uses a CAS loop with `compare_set_weak`, which uses
415    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
416    /// eventual consistency even under high contention.
417    ///
418    /// # Parameters
419    ///
420    /// * `f` - A function that takes the current value and returns the new
421    ///   value.
422    ///
423    /// # Returns
424    ///
425    /// The old value before the update.
426    ///
427    /// The closure may be called more than once when concurrent updates cause
428    /// CAS retries.
429    #[inline]
430    pub fn fetch_update<F>(&self, mut f: F) -> f64
431    where
432        F: FnMut(f64) -> f64,
433    {
434        let mut current = self.load();
435        loop {
436            let new = f(current);
437            match self.compare_set_weak(current, new) {
438                Ok(_) => return current,
439                Err(actual) => current = actual,
440            }
441        }
442    }
443
444    /// Updates the value using a function, returning the new value.
445    ///
446    /// Internally uses a CAS loop until the update succeeds.
447    ///
448    /// # Parameters
449    ///
450    /// * `f` - A function that takes the current value and returns the new
451    ///   value.
452    ///
453    /// # Returns
454    ///
455    /// The value committed by the successful update.
456    ///
457    /// The closure may be called more than once when concurrent updates cause
458    /// CAS retries.
459    #[inline]
460    pub fn update_and_get<F>(&self, mut f: F) -> f64
461    where
462        F: FnMut(f64) -> f64,
463    {
464        let mut current = self.load();
465        loop {
466            let new = f(current);
467            match self.compare_set_weak(current, new) {
468                Ok(_) => return new,
469                Err(actual) => current = actual,
470            }
471        }
472    }
473
474    /// Conditionally updates the value using a function.
475    ///
476    /// Internally uses a CAS loop until the update succeeds or the closure
477    /// rejects the current value by returning `None`.
478    ///
479    /// # Parameters
480    ///
481    /// * `f` - A function that takes the current value and returns the new
482    ///   value, or `None` to leave the value unchanged.
483    ///
484    /// # Returns
485    ///
486    /// `Some(old_value)` when the update succeeds, or `None` when `f` rejects
487    /// the observed current value.
488    ///
489    /// The closure may be called more than once when concurrent updates cause
490    /// CAS retries.
491    #[inline]
492    pub fn try_update<F>(&self, mut f: F) -> Option<f64>
493    where
494        F: FnMut(f64) -> Option<f64>,
495    {
496        let mut current = self.load();
497        loop {
498            let new = f(current)?;
499            match self.compare_set_weak(current, new) {
500                Ok(_) => return Some(current),
501                Err(actual) => current = actual,
502            }
503        }
504    }
505
506    /// Conditionally updates the value using a function, returning the new value.
507    ///
508    /// Internally uses a CAS loop until the update succeeds or the closure
509    /// rejects the current value by returning `None`.
510    ///
511    /// # Parameters
512    ///
513    /// * `f` - A function that takes the current value and returns the new
514    ///   value, or `None` to leave the value unchanged.
515    ///
516    /// # Returns
517    ///
518    /// `Some(new_value)` when the update succeeds, or `None` when `f` rejects
519    /// the observed current value.
520    ///
521    /// The closure may be called more than once when concurrent updates cause
522    /// CAS retries.
523    #[inline]
524    pub fn try_update_and_get<F>(&self, mut f: F) -> Option<f64>
525    where
526        F: FnMut(f64) -> Option<f64>,
527    {
528        let mut current = self.load();
529        loop {
530            let new = f(current)?;
531            match self.compare_set_weak(current, new) {
532                Ok(_) => return Some(new),
533                Err(actual) => current = actual,
534            }
535        }
536    }
537
538    /// Gets a reference to the underlying standard library atomic type.
539    ///
540    /// This allows direct access to the standard library's atomic operations
541    /// for advanced use cases that require fine-grained control over memory
542    /// ordering.
543    ///
544    /// # Memory Ordering
545    ///
546    /// When using the returned reference, you have full control over memory
547    /// ordering. Remember to use `f64::to_bits()` and `f64::from_bits()` for
548    /// conversions.
549    ///
550    /// # Returns
551    ///
552    /// A reference to the underlying `std::sync::atomic::AtomicU64`.
553    #[inline]
554    pub fn inner(&self) -> &AtomicU64 {
555        &self.inner
556    }
557}
558
559impl AtomicOps for AtomicF64 {
560    type Value = f64;
561
562    #[inline]
563    fn load(&self) -> f64 {
564        self.load()
565    }
566
567    #[inline]
568    fn store(&self, value: f64) {
569        self.store(value);
570    }
571
572    #[inline]
573    fn swap(&self, value: f64) -> f64 {
574        self.swap(value)
575    }
576
577    #[inline]
578    fn compare_set(&self, current: f64, new: f64) -> Result<(), f64> {
579        self.compare_set(current, new)
580    }
581
582    #[inline]
583    fn compare_set_weak(&self, current: f64, new: f64) -> Result<(), f64> {
584        self.compare_set_weak(current, new)
585    }
586
587    #[inline]
588    fn compare_exchange(&self, current: f64, new: f64) -> f64 {
589        self.compare_and_exchange(current, new)
590    }
591
592    #[inline]
593    fn compare_exchange_weak(&self, current: f64, new: f64) -> Result<f64, f64> {
594        self.compare_and_exchange_weak(current, new)
595    }
596
597    #[inline]
598    fn fetch_update<F>(&self, f: F) -> f64
599    where
600        F: FnMut(f64) -> f64,
601    {
602        self.fetch_update(f)
603    }
604
605    #[inline]
606    fn update_and_get<F>(&self, f: F) -> f64
607    where
608        F: FnMut(f64) -> f64,
609    {
610        self.update_and_get(f)
611    }
612
613    #[inline]
614    fn try_update<F>(&self, f: F) -> Option<f64>
615    where
616        F: FnMut(f64) -> Option<f64>,
617    {
618        self.try_update(f)
619    }
620
621    #[inline]
622    fn try_update_and_get<F>(&self, f: F) -> Option<f64>
623    where
624        F: FnMut(f64) -> Option<f64>,
625    {
626        self.try_update_and_get(f)
627    }
628}
629
630impl AtomicNumberOps for AtomicF64 {
631    #[inline]
632    fn fetch_add(&self, delta: f64) -> f64 {
633        self.fetch_add(delta)
634    }
635
636    #[inline]
637    fn fetch_sub(&self, delta: f64) -> f64 {
638        self.fetch_sub(delta)
639    }
640
641    #[inline]
642    fn fetch_mul(&self, factor: f64) -> f64 {
643        self.fetch_mul(factor)
644    }
645
646    #[inline]
647    fn fetch_div(&self, divisor: f64) -> f64 {
648        self.fetch_div(divisor)
649    }
650}