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}