Skip to main content

qubit_atomic/atomic/
atomic_f64.rs

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