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/// # Examples
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    /// # Examples
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    #[must_use]
116    #[inline(always)]
117    pub fn load(&self) -> f64 {
118        f64::from_bits(self.inner.load(Ordering::Acquire))
119    }
120
121    /// Sets a new value.
122    ///
123    /// # Memory Ordering
124    ///
125    /// Uses `Release` ordering on the underlying `AtomicU64`. This ensures
126    /// that all prior writes in this thread are visible to other threads
127    /// that perform an `Acquire` load.
128    ///
129    /// # Parameters
130    ///
131    /// * `value` - The new value to set.
132    #[inline(always)]
133    pub fn store(&self, value: f64) {
134        self.inner.store(value.to_bits(), Ordering::Release);
135    }
136
137    /// Swaps the current value with a new value, returning the old value.
138    ///
139    /// # Memory Ordering
140    ///
141    /// Uses `AcqRel` ordering on the underlying `AtomicU64`. This provides
142    /// full synchronization for this read-modify-write operation.
143    ///
144    /// # Parameters
145    ///
146    /// * `value` - The new value to swap in.
147    ///
148    /// # Returns
149    ///
150    /// The old value.
151    #[must_use]
152    #[inline(always)]
153    pub fn swap(&self, value: f64) -> f64 {
154        f64::from_bits(self.inner.swap(value.to_bits(), Ordering::AcqRel))
155    }
156
157    /// Compares and sets the value atomically.
158    ///
159    /// If the current value equals `current`, sets it to `new` and returns
160    /// `Ok(())`. Otherwise, returns `Err(actual)` where `actual` is the
161    /// current value.
162    ///
163    /// Comparison uses the exact raw bit pattern produced by
164    /// [`f64::to_bits`], not [`PartialEq`].
165    ///
166    /// # Memory Ordering
167    ///
168    /// - **Success**: Uses `AcqRel` ordering on the underlying `AtomicU64` to
169    ///   ensure full synchronization when the exchange succeeds.
170    /// - **Failure**: Uses `Acquire` ordering to observe the actual value
171    ///   written by another thread.
172    ///
173    /// # Parameters
174    ///
175    /// * `current` - The expected current value.
176    /// * `new` - The new value to set if current matches.
177    ///
178    /// # Returns
179    ///
180    /// `Ok(())` when the value was replaced.
181    ///
182    /// # Errors
183    ///
184    /// Returns `Err(actual)` with the observed value when the raw-bit
185    /// comparison fails. In that case, `new` is not stored.
186    #[inline(always)]
187    pub fn compare_set(&self, current: f64, new: f64) -> Result<(), f64> {
188        self.inner
189            .compare_exchange(
190                current.to_bits(),
191                new.to_bits(),
192                Ordering::AcqRel,
193                Ordering::Acquire,
194            )
195            .map(|_| ())
196            .map_err(f64::from_bits)
197    }
198
199    /// Weak version of compare-and-set.
200    ///
201    /// May spuriously fail even when the comparison succeeds. Should be used
202    /// in a loop.
203    ///
204    /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
205    /// Comparison uses the exact raw bit pattern produced by
206    /// [`f64::to_bits`].
207    ///
208    /// # Parameters
209    ///
210    /// * `current` - The expected current value.
211    /// * `new` - The new value to set if current matches.
212    ///
213    /// # Returns
214    ///
215    /// `Ok(())` when the value was replaced.
216    ///
217    /// # Errors
218    ///
219    /// Returns `Err(actual)` with the observed value when the raw-bit
220    /// comparison fails, including possible spurious failures. In that case,
221    /// `new` is not stored.
222    #[inline(always)]
223    pub fn compare_set_weak(&self, current: f64, new: f64) -> Result<(), f64> {
224        self.inner
225            .compare_exchange_weak(
226                current.to_bits(),
227                new.to_bits(),
228                Ordering::AcqRel,
229                Ordering::Acquire,
230            )
231            .map(|_| ())
232            .map_err(f64::from_bits)
233    }
234
235    /// Compares and exchanges the value atomically, returning the previous
236    /// value.
237    ///
238    /// If the current value equals `current`, sets it to `new` and returns
239    /// the old value. Otherwise, returns the actual current value.
240    ///
241    /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
242    ///
243    /// # Parameters
244    ///
245    /// * `current` - The expected current value.
246    /// * `new` - The new value to set if current matches.
247    ///
248    /// # Returns
249    ///
250    /// The value observed before the operation completed. If the returned
251    /// value has the same raw bits as `current`, the exchange succeeded;
252    /// otherwise it is the actual value that prevented the exchange.
253    #[must_use]
254    #[inline]
255    pub fn compare_and_exchange(&self, current: f64, new: f64) -> f64 {
256        match self.inner.compare_exchange(
257            current.to_bits(),
258            new.to_bits(),
259            Ordering::AcqRel,
260            Ordering::Acquire,
261        ) {
262            Ok(prev_bits) => f64::from_bits(prev_bits),
263            Err(actual_bits) => f64::from_bits(actual_bits),
264        }
265    }
266
267    /// Weak version of compare-and-exchange.
268    ///
269    /// May spuriously fail even when the comparison succeeds. Should be used
270    /// in a loop.
271    ///
272    /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
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(previous)` when the value was replaced, or `Err(actual)` when the
282    /// comparison failed, including possible spurious failure. Values preserve
283    /// their exact raw bit patterns.
284    #[inline(always)]
285    pub fn compare_and_exchange_weak(
286        &self,
287        current: f64,
288        new: f64,
289    ) -> Result<f64, f64> {
290        self.inner
291            .compare_exchange_weak(
292                current.to_bits(),
293                new.to_bits(),
294                Ordering::AcqRel,
295                Ordering::Acquire,
296            )
297            .map(f64::from_bits)
298            .map_err(f64::from_bits)
299    }
300
301    /// Atomically adds a value, returning the old value.
302    ///
303    /// # Memory Ordering
304    ///
305    /// Internally uses a CAS loop with `compare_set_weak`, which uses
306    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
307    /// eventual consistency even under high contention.
308    ///
309    /// # Performance
310    ///
311    /// May be slow in high-contention scenarios due to the CAS loop.
312    /// Consider using atomic integers if performance is critical.
313    ///
314    /// # Parameters
315    ///
316    /// * `delta` - The value to add.
317    ///
318    /// # Returns
319    ///
320    /// The old value before adding.
321    ///
322    /// # Examples
323    ///
324    /// ```rust
325    /// use qubit_atomic::Atomic;
326    ///
327    /// let atomic = Atomic::<f64>::new(10.0);
328    /// let old = atomic.fetch_add(5.5);
329    /// assert_eq!(old, 10.0);
330    /// assert_eq!(atomic.load(), 15.5);
331    /// ```
332    #[inline(always)]
333    pub fn fetch_add(&self, delta: f64) -> f64 {
334        self.fetch_update(|current| current + delta)
335    }
336
337    /// Atomically subtracts a value, returning the old value.
338    ///
339    /// # Memory Ordering
340    ///
341    /// Internally uses a CAS loop with `compare_set_weak`, which uses
342    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
343    /// eventual consistency even under high contention.
344    ///
345    /// # Parameters
346    ///
347    /// * `delta` - The value to subtract.
348    ///
349    /// # Returns
350    ///
351    /// The old value before subtracting.
352    ///
353    /// # Examples
354    ///
355    /// ```rust
356    /// use qubit_atomic::Atomic;
357    ///
358    /// let atomic = Atomic::<f64>::new(10.0);
359    /// let old = atomic.fetch_sub(3.5);
360    /// assert_eq!(old, 10.0);
361    /// assert_eq!(atomic.load(), 6.5);
362    /// ```
363    #[inline(always)]
364    pub fn fetch_sub(&self, delta: f64) -> f64 {
365        self.fetch_update(|current| current - delta)
366    }
367
368    /// Atomically multiplies by a factor, returning the old value.
369    ///
370    /// # Memory Ordering
371    ///
372    /// Internally uses a CAS loop with `compare_set_weak`, which uses
373    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
374    /// eventual consistency even under high contention.
375    ///
376    /// # Parameters
377    ///
378    /// * `factor` - The factor to multiply by.
379    ///
380    /// # Returns
381    ///
382    /// The old value before multiplying.
383    ///
384    /// # Examples
385    ///
386    /// ```rust
387    /// use qubit_atomic::Atomic;
388    ///
389    /// let atomic = Atomic::<f64>::new(10.0);
390    /// let old = atomic.fetch_mul(2.5);
391    /// assert_eq!(old, 10.0);
392    /// assert_eq!(atomic.load(), 25.0);
393    /// ```
394    #[inline(always)]
395    pub fn fetch_mul(&self, factor: f64) -> f64 {
396        self.fetch_update(|current| current * factor)
397    }
398
399    /// Atomically divides by a divisor, returning the old value.
400    ///
401    /// # Memory Ordering
402    ///
403    /// Internally uses a CAS loop with `compare_set_weak`, which uses
404    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
405    /// eventual consistency even under high contention.
406    ///
407    /// # Parameters
408    ///
409    /// * `divisor` - The divisor to divide by.
410    ///
411    /// # Returns
412    ///
413    /// The old value before dividing.
414    ///
415    /// # Examples
416    ///
417    /// ```rust
418    /// use qubit_atomic::Atomic;
419    ///
420    /// let atomic = Atomic::<f64>::new(10.0);
421    /// let old = atomic.fetch_div(2.0);
422    /// assert_eq!(old, 10.0);
423    /// assert_eq!(atomic.load(), 5.0);
424    /// ```
425    #[inline(always)]
426    pub fn fetch_div(&self, divisor: f64) -> f64 {
427        self.fetch_update(|current| current / divisor)
428    }
429
430    /// Updates the value using a function, returning the old value.
431    ///
432    /// # Memory Ordering
433    ///
434    /// Internally uses a CAS loop with `compare_set_weak`, which uses
435    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
436    /// eventual consistency even under high contention.
437    ///
438    /// # Parameters
439    ///
440    /// * `f` - A function that takes the current value and returns the new
441    ///   value.
442    ///
443    /// # Returns
444    ///
445    /// The old value before the update.
446    ///
447    /// The closure may be called more than once when concurrent updates cause
448    /// CAS retries.
449    pub fn fetch_update<F>(&self, mut f: F) -> f64
450    where
451        F: FnMut(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    /// Updates the value using a function, returning the new value.
464    ///
465    /// Internally uses a CAS loop until the update succeeds.
466    ///
467    /// # Parameters
468    ///
469    /// * `f` - A function that takes the current value and returns the new
470    ///   value.
471    ///
472    /// # Returns
473    ///
474    /// The value committed by the successful update.
475    ///
476    /// The closure may be called more than once when concurrent updates cause
477    /// CAS retries.
478    pub fn update_and_get<F>(&self, mut f: F) -> f64
479    where
480        F: FnMut(f64) -> f64,
481    {
482        let mut current = self.load();
483        loop {
484            let new = f(current);
485            match self.compare_set_weak(current, new) {
486                Ok(_) => return new,
487                Err(actual) => current = actual,
488            }
489        }
490    }
491
492    /// Conditionally updates the value using a function.
493    ///
494    /// Internally uses a CAS loop until the update succeeds or the closure
495    /// rejects the current value by returning `None`.
496    ///
497    /// # Parameters
498    ///
499    /// * `f` - A function that takes the current value and returns the new
500    ///   value, or `None` to leave the value unchanged.
501    ///
502    /// # Returns
503    ///
504    /// `Some(old_value)` when the update succeeds, or `None` when `f` rejects
505    /// the observed current value.
506    ///
507    /// The closure may be called more than once when concurrent updates cause
508    /// CAS retries.
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    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    #[must_use]
571    #[inline(always)]
572    pub fn inner(&self) -> &AtomicU64 {
573        &self.inner
574    }
575}
576
577impl AtomicOps for AtomicF64 {
578    type Value = f64;
579
580    #[inline(always)]
581    fn load(&self) -> f64 {
582        self.load()
583    }
584
585    #[inline(always)]
586    fn store(&self, value: f64) {
587        self.store(value);
588    }
589
590    #[inline(always)]
591    fn swap(&self, value: f64) -> f64 {
592        self.swap(value)
593    }
594
595    #[inline(always)]
596    fn compare_set(&self, current: f64, new: f64) -> Result<(), f64> {
597        self.compare_set(current, new)
598    }
599
600    #[inline(always)]
601    fn compare_set_weak(&self, current: f64, new: f64) -> Result<(), f64> {
602        self.compare_set_weak(current, new)
603    }
604
605    #[inline(always)]
606    fn compare_exchange(&self, current: f64, new: f64) -> f64 {
607        self.compare_and_exchange(current, new)
608    }
609
610    #[inline(always)]
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(always)]
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(always)]
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(always)]
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(always)]
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(always)]
654    fn fetch_add(&self, delta: f64) -> f64 {
655        self.fetch_add(delta)
656    }
657
658    #[inline(always)]
659    fn fetch_sub(&self, delta: f64) -> f64 {
660        self.fetch_sub(delta)
661    }
662
663    #[inline(always)]
664    fn fetch_mul(&self, factor: f64) -> f64 {
665        self.fetch_mul(factor)
666    }
667
668    #[inline(always)]
669    fn fetch_div(&self, divisor: f64) -> f64 {
670        self.fetch_div(divisor)
671    }
672}