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}