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(
192                current.to_bits(),
193                new.to_bits(),
194                Ordering::AcqRel,
195                Ordering::Acquire,
196            )
197            .map(|_| ())
198            .map_err(f64::from_bits)
199    }
200
201    /// Weak version of compare-and-set.
202    ///
203    /// May spuriously fail even when the comparison succeeds. Should be used
204    /// in a loop.
205    ///
206    /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
207    /// Comparison uses the exact raw bit pattern produced by
208    /// [`f64::to_bits`].
209    ///
210    /// # Parameters
211    ///
212    /// * `current` - The expected current value.
213    /// * `new` - The new value to set if current matches.
214    ///
215    /// # Returns
216    ///
217    /// `Ok(())` when the value was replaced.
218    ///
219    /// # Errors
220    ///
221    /// Returns `Err(actual)` with the observed value when the raw-bit
222    /// comparison fails, including possible spurious failures. In that case,
223    /// `new` is not stored.
224    #[inline]
225    pub fn compare_set_weak(&self, current: f64, new: f64) -> Result<(), f64> {
226        self.inner
227            .compare_exchange_weak(
228                current.to_bits(),
229                new.to_bits(),
230                Ordering::AcqRel,
231                Ordering::Acquire,
232            )
233            .map(|_| ())
234            .map_err(f64::from_bits)
235    }
236
237    /// Compares and exchanges the value atomically, returning the previous
238    /// value.
239    ///
240    /// If the current value equals `current`, sets it to `new` and returns
241    /// the old value. Otherwise, returns the actual current value.
242    ///
243    /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
244    ///
245    /// # Parameters
246    ///
247    /// * `current` - The expected current value.
248    /// * `new` - The new value to set if current matches.
249    ///
250    /// # Returns
251    ///
252    /// The value observed before the operation completed. If the returned
253    /// value has the same raw bits as `current`, the exchange succeeded;
254    /// otherwise it is the actual value that prevented the exchange.
255    #[inline]
256    pub fn compare_and_exchange(&self, current: f64, new: f64) -> f64 {
257        match self.inner.compare_exchange(
258            current.to_bits(),
259            new.to_bits(),
260            Ordering::AcqRel,
261            Ordering::Acquire,
262        ) {
263            Ok(prev_bits) => f64::from_bits(prev_bits),
264            Err(actual_bits) => f64::from_bits(actual_bits),
265        }
266    }
267
268    /// Weak version of compare-and-exchange.
269    ///
270    /// May spuriously fail even when the comparison succeeds. Should be used
271    /// in a loop.
272    ///
273    /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
274    ///
275    /// # Parameters
276    ///
277    /// * `current` - The expected current value.
278    /// * `new` - The new value to set if current matches.
279    ///
280    /// # Returns
281    ///
282    /// `Ok(previous)` when the value was replaced, or `Err(actual)` when the
283    /// comparison failed, including possible spurious failure. Values preserve
284    /// their exact raw bit patterns.
285    #[inline]
286    pub fn compare_and_exchange_weak(&self, current: f64, new: f64) -> 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 value.
524    ///
525    /// Internally uses a CAS loop until the update succeeds or the closure
526    /// rejects the current value by returning `None`.
527    ///
528    /// # Parameters
529    ///
530    /// * `f` - A function that takes the current value and returns the new
531    ///   value, or `None` to leave the value unchanged.
532    ///
533    /// # Returns
534    ///
535    /// `Some(new_value)` when the update succeeds, or `None` when `f` rejects
536    /// the observed current value.
537    ///
538    /// The closure may be called more than once when concurrent updates cause
539    /// CAS retries.
540    #[inline]
541    pub fn try_update_and_get<F>(&self, mut f: F) -> Option<f64>
542    where
543        F: FnMut(f64) -> Option<f64>,
544    {
545        let mut current = self.load();
546        loop {
547            let new = f(current)?;
548            match self.compare_set_weak(current, new) {
549                Ok(_) => return Some(new),
550                Err(actual) => current = actual,
551            }
552        }
553    }
554
555    /// Gets a reference to the underlying standard library atomic type.
556    ///
557    /// This allows direct access to the standard library's atomic operations
558    /// for advanced use cases that require fine-grained control over memory
559    /// ordering.
560    ///
561    /// # Memory Ordering
562    ///
563    /// When using the returned reference, you have full control over memory
564    /// ordering. Remember to use `f64::to_bits()` and `f64::from_bits()` for
565    /// conversions.
566    ///
567    /// # Returns
568    ///
569    /// A reference to the underlying `std::sync::atomic::AtomicU64`.
570    #[inline]
571    pub fn inner(&self) -> &AtomicU64 {
572        &self.inner
573    }
574}
575
576impl AtomicOps for AtomicF64 {
577    type Value = f64;
578
579    #[inline]
580    fn load(&self) -> f64 {
581        self.load()
582    }
583
584    #[inline]
585    fn store(&self, value: f64) {
586        self.store(value);
587    }
588
589    #[inline]
590    fn swap(&self, value: f64) -> f64 {
591        self.swap(value)
592    }
593
594    #[inline]
595    fn compare_set(&self, current: f64, new: f64) -> Result<(), f64> {
596        self.compare_set(current, new)
597    }
598
599    #[inline]
600    fn compare_set_weak(&self, current: f64, new: f64) -> Result<(), f64> {
601        self.compare_set_weak(current, new)
602    }
603
604    #[inline]
605    fn compare_exchange(&self, current: f64, new: f64) -> f64 {
606        self.compare_and_exchange(current, new)
607    }
608
609    #[inline]
610    fn compare_exchange_weak(&self, current: f64, new: f64) -> Result<f64, f64> {
611        self.compare_and_exchange_weak(current, new)
612    }
613
614    #[inline]
615    fn fetch_update<F>(&self, f: F) -> f64
616    where
617        F: FnMut(f64) -> f64,
618    {
619        self.fetch_update(f)
620    }
621
622    #[inline]
623    fn update_and_get<F>(&self, f: F) -> f64
624    where
625        F: FnMut(f64) -> f64,
626    {
627        self.update_and_get(f)
628    }
629
630    #[inline]
631    fn try_update<F>(&self, f: F) -> Option<f64>
632    where
633        F: FnMut(f64) -> Option<f64>,
634    {
635        self.try_update(f)
636    }
637
638    #[inline]
639    fn try_update_and_get<F>(&self, f: F) -> Option<f64>
640    where
641        F: FnMut(f64) -> Option<f64>,
642    {
643        self.try_update_and_get(f)
644    }
645}
646
647impl AtomicNumberOps for AtomicF64 {
648    #[inline]
649    fn fetch_add(&self, delta: f64) -> f64 {
650        self.fetch_add(delta)
651    }
652
653    #[inline]
654    fn fetch_sub(&self, delta: f64) -> f64 {
655        self.fetch_sub(delta)
656    }
657
658    #[inline]
659    fn fetch_mul(&self, factor: f64) -> f64 {
660        self.fetch_mul(factor)
661    }
662
663    #[inline]
664    fn fetch_div(&self, divisor: f64) -> f64 {
665        self.fetch_div(divisor)
666    }
667}