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    /// The value observed before the operation completed. Because this
283    /// operation may fail spuriously, a returned value with the same raw bits
284    /// as `current` does not by itself prove that `new` was stored; use
285    /// [`compare_set_weak`](Self::compare_set_weak) when the caller needs an
286    /// explicit success indicator.
287    #[inline]
288    pub fn compare_and_exchange_weak(&self, current: f64, new: f64) -> f64 {
289        match self.inner.compare_exchange_weak(
290            current.to_bits(),
291            new.to_bits(),
292            Ordering::AcqRel,
293            Ordering::Acquire,
294        ) {
295            Ok(prev_bits) => f64::from_bits(prev_bits),
296            Err(actual_bits) => f64::from_bits(actual_bits),
297        }
298    }
299
300    /// Atomically adds a value, returning the old value.
301    ///
302    /// # Memory Ordering
303    ///
304    /// Internally uses a CAS loop with `compare_set_weak`, which uses
305    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
306    /// eventual consistency even under high contention.
307    ///
308    /// # Performance
309    ///
310    /// May be slow in high-contention scenarios due to the CAS loop.
311    /// Consider using atomic integers if performance is critical.
312    ///
313    /// # Parameters
314    ///
315    /// * `delta` - The value to add.
316    ///
317    /// # Returns
318    ///
319    /// The old value before adding.
320    ///
321    /// # Example
322    ///
323    /// ```rust
324    /// use qubit_atomic::Atomic;
325    ///
326    /// let atomic = Atomic::<f64>::new(10.0);
327    /// let old = atomic.fetch_add(5.5);
328    /// assert_eq!(old, 10.0);
329    /// assert_eq!(atomic.load(), 15.5);
330    /// ```
331    #[inline]
332    pub fn fetch_add(&self, delta: f64) -> f64 {
333        self.fetch_update(|current| current + delta)
334    }
335
336    /// Atomically subtracts a value, returning the old value.
337    ///
338    /// # Memory Ordering
339    ///
340    /// Internally uses a CAS loop with `compare_set_weak`, which uses
341    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
342    /// eventual consistency even under high contention.
343    ///
344    /// # Parameters
345    ///
346    /// * `delta` - The value to subtract.
347    ///
348    /// # Returns
349    ///
350    /// The old value before subtracting.
351    ///
352    /// # Example
353    ///
354    /// ```rust
355    /// use qubit_atomic::Atomic;
356    ///
357    /// let atomic = Atomic::<f64>::new(10.0);
358    /// let old = atomic.fetch_sub(3.5);
359    /// assert_eq!(old, 10.0);
360    /// assert_eq!(atomic.load(), 6.5);
361    /// ```
362    #[inline]
363    pub fn fetch_sub(&self, delta: f64) -> f64 {
364        self.fetch_update(|current| current - delta)
365    }
366
367    /// Atomically multiplies by a factor, returning the old value.
368    ///
369    /// # Memory Ordering
370    ///
371    /// Internally uses a CAS loop with `compare_set_weak`, which uses
372    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
373    /// eventual consistency even under high contention.
374    ///
375    /// # Parameters
376    ///
377    /// * `factor` - The factor to multiply by.
378    ///
379    /// # Returns
380    ///
381    /// The old value before multiplying.
382    ///
383    /// # Example
384    ///
385    /// ```rust
386    /// use qubit_atomic::Atomic;
387    ///
388    /// let atomic = Atomic::<f64>::new(10.0);
389    /// let old = atomic.fetch_mul(2.5);
390    /// assert_eq!(old, 10.0);
391    /// assert_eq!(atomic.load(), 25.0);
392    /// ```
393    #[inline]
394    pub fn fetch_mul(&self, factor: f64) -> f64 {
395        self.fetch_update(|current| current * factor)
396    }
397
398    /// Atomically divides by a divisor, returning the old value.
399    ///
400    /// # Memory Ordering
401    ///
402    /// Internally uses a CAS loop with `compare_set_weak`, which uses
403    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
404    /// eventual consistency even under high contention.
405    ///
406    /// # Parameters
407    ///
408    /// * `divisor` - The divisor to divide by.
409    ///
410    /// # Returns
411    ///
412    /// The old value before dividing.
413    ///
414    /// # Example
415    ///
416    /// ```rust
417    /// use qubit_atomic::Atomic;
418    ///
419    /// let atomic = Atomic::<f64>::new(10.0);
420    /// let old = atomic.fetch_div(2.0);
421    /// assert_eq!(old, 10.0);
422    /// assert_eq!(atomic.load(), 5.0);
423    /// ```
424    #[inline]
425    pub fn fetch_div(&self, divisor: f64) -> f64 {
426        self.fetch_update(|current| current / divisor)
427    }
428
429    /// Updates the value using a function, returning the old value.
430    ///
431    /// # Memory Ordering
432    ///
433    /// Internally uses a CAS loop with `compare_set_weak`, which uses
434    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
435    /// eventual consistency even under high contention.
436    ///
437    /// # Parameters
438    ///
439    /// * `f` - A function that takes the current value and returns the new
440    ///   value.
441    ///
442    /// # Returns
443    ///
444    /// The old value before the update.
445    ///
446    /// The closure may be called more than once when concurrent updates cause
447    /// CAS retries.
448    #[inline]
449    pub fn fetch_update<F>(&self, f: F) -> f64
450    where
451        F: Fn(f64) -> f64,
452    {
453        let mut current = self.load();
454        loop {
455            let new = f(current);
456            match self.compare_set_weak(current, new) {
457                Ok(_) => return current,
458                Err(actual) => current = actual,
459            }
460        }
461    }
462
463    /// Conditionally updates the value using a function.
464    ///
465    /// Internally uses a CAS loop until the update succeeds or the closure
466    /// rejects the current value by returning `None`.
467    ///
468    /// # Parameters
469    ///
470    /// * `f` - A function that takes the current value and returns the new
471    ///   value, or `None` to leave the value unchanged.
472    ///
473    /// # Returns
474    ///
475    /// `Some(old_value)` when the update succeeds, or `None` when `f` rejects
476    /// the observed current value.
477    ///
478    /// The closure may be called more than once when concurrent updates cause
479    /// CAS retries.
480    #[inline]
481    pub fn try_update<F>(&self, f: F) -> Option<f64>
482    where
483        F: Fn(f64) -> Option<f64>,
484    {
485        let mut current = self.load();
486        loop {
487            let new = f(current)?;
488            match self.compare_set_weak(current, new) {
489                Ok(_) => return Some(current),
490                Err(actual) => current = actual,
491            }
492        }
493    }
494
495    /// Gets a reference to the underlying standard library atomic type.
496    ///
497    /// This allows direct access to the standard library's atomic operations
498    /// for advanced use cases that require fine-grained control over memory
499    /// ordering.
500    ///
501    /// # Memory Ordering
502    ///
503    /// When using the returned reference, you have full control over memory
504    /// ordering. Remember to use `f64::to_bits()` and `f64::from_bits()` for
505    /// conversions.
506    ///
507    /// # Returns
508    ///
509    /// A reference to the underlying `std::sync::atomic::AtomicU64`.
510    #[inline]
511    pub fn inner(&self) -> &AtomicU64 {
512        &self.inner
513    }
514}
515
516impl AtomicOps for AtomicF64 {
517    type Value = f64;
518
519    #[inline]
520    fn load(&self) -> f64 {
521        self.load()
522    }
523
524    #[inline]
525    fn store(&self, value: f64) {
526        self.store(value);
527    }
528
529    #[inline]
530    fn swap(&self, value: f64) -> f64 {
531        self.swap(value)
532    }
533
534    #[inline]
535    fn compare_set(&self, current: f64, new: f64) -> Result<(), f64> {
536        self.compare_set(current, new)
537    }
538
539    #[inline]
540    fn compare_set_weak(&self, current: f64, new: f64) -> Result<(), f64> {
541        self.compare_set_weak(current, new)
542    }
543
544    #[inline]
545    fn compare_exchange(&self, current: f64, new: f64) -> f64 {
546        self.compare_and_exchange(current, new)
547    }
548
549    #[inline]
550    fn compare_exchange_weak(&self, current: f64, new: f64) -> f64 {
551        self.compare_and_exchange_weak(current, new)
552    }
553
554    #[inline]
555    fn fetch_update<F>(&self, f: F) -> f64
556    where
557        F: Fn(f64) -> f64,
558    {
559        self.fetch_update(f)
560    }
561
562    #[inline]
563    fn try_update<F>(&self, f: F) -> Option<f64>
564    where
565        F: Fn(f64) -> Option<f64>,
566    {
567        self.try_update(f)
568    }
569}
570
571impl AtomicNumberOps for AtomicF64 {
572    #[inline]
573    fn fetch_add(&self, delta: f64) -> f64 {
574        self.fetch_add(delta)
575    }
576
577    #[inline]
578    fn fetch_sub(&self, delta: f64) -> f64 {
579        self.fetch_sub(delta)
580    }
581
582    #[inline]
583    fn fetch_mul(&self, factor: f64) -> f64 {
584        self.fetch_mul(factor)
585    }
586
587    #[inline]
588    fn fetch_div(&self, divisor: f64) -> f64 {
589        self.fetch_div(divisor)
590    }
591}