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(current.to_bits(), new.to_bits(), Ordering::AcqRel, Ordering::Acquire)
192 .map(|_| ())
193 .map_err(f64::from_bits)
194 }
195
196 /// Weak version of compare-and-set.
197 ///
198 /// May spuriously fail even when the comparison succeeds. Should be used
199 /// in a loop.
200 ///
201 /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
202 /// Comparison uses the exact raw bit pattern produced by
203 /// [`f64::to_bits`].
204 ///
205 /// # Parameters
206 ///
207 /// * `current` - The expected current value.
208 /// * `new` - The new value to set if current matches.
209 ///
210 /// # Returns
211 ///
212 /// `Ok(())` when the value was replaced.
213 ///
214 /// # Errors
215 ///
216 /// Returns `Err(actual)` with the observed value when the raw-bit
217 /// comparison fails, including possible spurious failures. In that case,
218 /// `new` is not stored.
219 #[inline]
220 pub fn compare_set_weak(&self, current: f64, new: f64) -> Result<(), f64> {
221 self.inner
222 .compare_exchange_weak(current.to_bits(), new.to_bits(), Ordering::AcqRel, Ordering::Acquire)
223 .map(|_| ())
224 .map_err(f64::from_bits)
225 }
226
227 /// Compares and exchanges the value atomically, returning the previous
228 /// value.
229 ///
230 /// If the current value equals `current`, sets it to `new` and returns
231 /// the old value. Otherwise, returns the actual current value.
232 ///
233 /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
234 ///
235 /// # Parameters
236 ///
237 /// * `current` - The expected current value.
238 /// * `new` - The new value to set if current matches.
239 ///
240 /// # Returns
241 ///
242 /// The value observed before the operation completed. If the returned
243 /// value has the same raw bits as `current`, the exchange succeeded;
244 /// otherwise it is the actual value that prevented the exchange.
245 #[inline]
246 pub fn compare_and_exchange(&self, current: f64, new: f64) -> f64 {
247 match self
248 .inner
249 .compare_exchange(current.to_bits(), new.to_bits(), Ordering::AcqRel, Ordering::Acquire)
250 {
251 Ok(prev_bits) => f64::from_bits(prev_bits),
252 Err(actual_bits) => f64::from_bits(actual_bits),
253 }
254 }
255
256 /// Weak version of compare-and-exchange.
257 ///
258 /// May spuriously fail even when the comparison succeeds. Should be used
259 /// in a loop.
260 ///
261 /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
262 ///
263 /// # Parameters
264 ///
265 /// * `current` - The expected current value.
266 /// * `new` - The new value to set if current matches.
267 ///
268 /// # Returns
269 ///
270 /// `Ok(previous)` when the value was replaced, or `Err(actual)` when the
271 /// comparison failed, including possible spurious failure. Values preserve
272 /// their exact raw bit patterns.
273 #[inline]
274 pub fn compare_and_exchange_weak(&self, current: f64, new: f64) -> Result<f64, f64> {
275 self.inner
276 .compare_exchange_weak(current.to_bits(), new.to_bits(), Ordering::AcqRel, Ordering::Acquire)
277 .map(f64::from_bits)
278 .map_err(f64::from_bits)
279 }
280
281 /// Atomically adds a value, returning the old value.
282 ///
283 /// # Memory Ordering
284 ///
285 /// Internally uses a CAS loop with `compare_set_weak`, which uses
286 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
287 /// eventual consistency even under high contention.
288 ///
289 /// # Performance
290 ///
291 /// May be slow in high-contention scenarios due to the CAS loop.
292 /// Consider using atomic integers if performance is critical.
293 ///
294 /// # Parameters
295 ///
296 /// * `delta` - The value to add.
297 ///
298 /// # Returns
299 ///
300 /// The old value before adding.
301 ///
302 /// # Example
303 ///
304 /// ```rust
305 /// use qubit_atomic::Atomic;
306 ///
307 /// let atomic = Atomic::<f64>::new(10.0);
308 /// let old = atomic.fetch_add(5.5);
309 /// assert_eq!(old, 10.0);
310 /// assert_eq!(atomic.load(), 15.5);
311 /// ```
312 #[inline]
313 pub fn fetch_add(&self, delta: f64) -> f64 {
314 self.fetch_update(|current| current + delta)
315 }
316
317 /// Atomically subtracts a value, returning the old value.
318 ///
319 /// # Memory Ordering
320 ///
321 /// Internally uses a CAS loop with `compare_set_weak`, which uses
322 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
323 /// eventual consistency even under high contention.
324 ///
325 /// # Parameters
326 ///
327 /// * `delta` - The value to subtract.
328 ///
329 /// # Returns
330 ///
331 /// The old value before subtracting.
332 ///
333 /// # Example
334 ///
335 /// ```rust
336 /// use qubit_atomic::Atomic;
337 ///
338 /// let atomic = Atomic::<f64>::new(10.0);
339 /// let old = atomic.fetch_sub(3.5);
340 /// assert_eq!(old, 10.0);
341 /// assert_eq!(atomic.load(), 6.5);
342 /// ```
343 #[inline]
344 pub fn fetch_sub(&self, delta: f64) -> f64 {
345 self.fetch_update(|current| current - delta)
346 }
347
348 /// Atomically multiplies by a factor, returning the old value.
349 ///
350 /// # Memory Ordering
351 ///
352 /// Internally uses a CAS loop with `compare_set_weak`, which uses
353 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
354 /// eventual consistency even under high contention.
355 ///
356 /// # Parameters
357 ///
358 /// * `factor` - The factor to multiply by.
359 ///
360 /// # Returns
361 ///
362 /// The old value before multiplying.
363 ///
364 /// # Example
365 ///
366 /// ```rust
367 /// use qubit_atomic::Atomic;
368 ///
369 /// let atomic = Atomic::<f64>::new(10.0);
370 /// let old = atomic.fetch_mul(2.5);
371 /// assert_eq!(old, 10.0);
372 /// assert_eq!(atomic.load(), 25.0);
373 /// ```
374 #[inline]
375 pub fn fetch_mul(&self, factor: f64) -> f64 {
376 self.fetch_update(|current| current * factor)
377 }
378
379 /// Atomically divides by a divisor, returning the old value.
380 ///
381 /// # Memory Ordering
382 ///
383 /// Internally uses a CAS loop with `compare_set_weak`, which uses
384 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
385 /// eventual consistency even under high contention.
386 ///
387 /// # Parameters
388 ///
389 /// * `divisor` - The divisor to divide by.
390 ///
391 /// # Returns
392 ///
393 /// The old value before dividing.
394 ///
395 /// # Example
396 ///
397 /// ```rust
398 /// use qubit_atomic::Atomic;
399 ///
400 /// let atomic = Atomic::<f64>::new(10.0);
401 /// let old = atomic.fetch_div(2.0);
402 /// assert_eq!(old, 10.0);
403 /// assert_eq!(atomic.load(), 5.0);
404 /// ```
405 #[inline]
406 pub fn fetch_div(&self, divisor: f64) -> f64 {
407 self.fetch_update(|current| current / divisor)
408 }
409
410 /// Updates the value using a function, returning the old value.
411 ///
412 /// # Memory Ordering
413 ///
414 /// Internally uses a CAS loop with `compare_set_weak`, which uses
415 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
416 /// eventual consistency even under high contention.
417 ///
418 /// # Parameters
419 ///
420 /// * `f` - A function that takes the current value and returns the new
421 /// value.
422 ///
423 /// # Returns
424 ///
425 /// The old value before the update.
426 ///
427 /// The closure may be called more than once when concurrent updates cause
428 /// CAS retries.
429 #[inline]
430 pub fn fetch_update<F>(&self, mut f: F) -> f64
431 where
432 F: FnMut(f64) -> f64,
433 {
434 let mut current = self.load();
435 loop {
436 let new = f(current);
437 match self.compare_set_weak(current, new) {
438 Ok(_) => return current,
439 Err(actual) => current = actual,
440 }
441 }
442 }
443
444 /// Updates the value using a function, returning the new value.
445 ///
446 /// Internally uses a CAS loop until the update succeeds.
447 ///
448 /// # Parameters
449 ///
450 /// * `f` - A function that takes the current value and returns the new
451 /// value.
452 ///
453 /// # Returns
454 ///
455 /// The value committed by the successful update.
456 ///
457 /// The closure may be called more than once when concurrent updates cause
458 /// CAS retries.
459 #[inline]
460 pub fn update_and_get<F>(&self, mut f: F) -> f64
461 where
462 F: FnMut(f64) -> f64,
463 {
464 let mut current = self.load();
465 loop {
466 let new = f(current);
467 match self.compare_set_weak(current, new) {
468 Ok(_) => return new,
469 Err(actual) => current = actual,
470 }
471 }
472 }
473
474 /// Conditionally updates the value using a function.
475 ///
476 /// Internally uses a CAS loop until the update succeeds or the closure
477 /// rejects the current value by returning `None`.
478 ///
479 /// # Parameters
480 ///
481 /// * `f` - A function that takes the current value and returns the new
482 /// value, or `None` to leave the value unchanged.
483 ///
484 /// # Returns
485 ///
486 /// `Some(old_value)` when the update succeeds, or `None` when `f` rejects
487 /// the observed current value.
488 ///
489 /// The closure may be called more than once when concurrent updates cause
490 /// CAS retries.
491 #[inline]
492 pub fn try_update<F>(&self, mut f: F) -> Option<f64>
493 where
494 F: FnMut(f64) -> Option<f64>,
495 {
496 let mut current = self.load();
497 loop {
498 let new = f(current)?;
499 match self.compare_set_weak(current, new) {
500 Ok(_) => return Some(current),
501 Err(actual) => current = actual,
502 }
503 }
504 }
505
506 /// Conditionally updates the value using a function, returning the new value.
507 ///
508 /// Internally uses a CAS loop until the update succeeds or the closure
509 /// rejects the current value by returning `None`.
510 ///
511 /// # Parameters
512 ///
513 /// * `f` - A function that takes the current value and returns the new
514 /// value, or `None` to leave the value unchanged.
515 ///
516 /// # Returns
517 ///
518 /// `Some(new_value)` when the update succeeds, or `None` when `f` rejects
519 /// the observed current value.
520 ///
521 /// The closure may be called more than once when concurrent updates cause
522 /// CAS retries.
523 #[inline]
524 pub fn try_update_and_get<F>(&self, mut f: F) -> Option<f64>
525 where
526 F: FnMut(f64) -> Option<f64>,
527 {
528 let mut current = self.load();
529 loop {
530 let new = f(current)?;
531 match self.compare_set_weak(current, new) {
532 Ok(_) => return Some(new),
533 Err(actual) => current = actual,
534 }
535 }
536 }
537
538 /// Gets a reference to the underlying standard library atomic type.
539 ///
540 /// This allows direct access to the standard library's atomic operations
541 /// for advanced use cases that require fine-grained control over memory
542 /// ordering.
543 ///
544 /// # Memory Ordering
545 ///
546 /// When using the returned reference, you have full control over memory
547 /// ordering. Remember to use `f64::to_bits()` and `f64::from_bits()` for
548 /// conversions.
549 ///
550 /// # Returns
551 ///
552 /// A reference to the underlying `std::sync::atomic::AtomicU64`.
553 #[inline]
554 pub fn inner(&self) -> &AtomicU64 {
555 &self.inner
556 }
557}
558
559impl AtomicOps for AtomicF64 {
560 type Value = f64;
561
562 #[inline]
563 fn load(&self) -> f64 {
564 self.load()
565 }
566
567 #[inline]
568 fn store(&self, value: f64) {
569 self.store(value);
570 }
571
572 #[inline]
573 fn swap(&self, value: f64) -> f64 {
574 self.swap(value)
575 }
576
577 #[inline]
578 fn compare_set(&self, current: f64, new: f64) -> Result<(), f64> {
579 self.compare_set(current, new)
580 }
581
582 #[inline]
583 fn compare_set_weak(&self, current: f64, new: f64) -> Result<(), f64> {
584 self.compare_set_weak(current, new)
585 }
586
587 #[inline]
588 fn compare_exchange(&self, current: f64, new: f64) -> f64 {
589 self.compare_and_exchange(current, new)
590 }
591
592 #[inline]
593 fn compare_exchange_weak(&self, current: f64, new: f64) -> Result<f64, f64> {
594 self.compare_and_exchange_weak(current, new)
595 }
596
597 #[inline]
598 fn fetch_update<F>(&self, f: F) -> f64
599 where
600 F: FnMut(f64) -> f64,
601 {
602 self.fetch_update(f)
603 }
604
605 #[inline]
606 fn update_and_get<F>(&self, f: F) -> f64
607 where
608 F: FnMut(f64) -> f64,
609 {
610 self.update_and_get(f)
611 }
612
613 #[inline]
614 fn try_update<F>(&self, f: F) -> Option<f64>
615 where
616 F: FnMut(f64) -> Option<f64>,
617 {
618 self.try_update(f)
619 }
620
621 #[inline]
622 fn try_update_and_get<F>(&self, f: F) -> Option<f64>
623 where
624 F: FnMut(f64) -> Option<f64>,
625 {
626 self.try_update_and_get(f)
627 }
628}
629
630impl AtomicNumberOps for AtomicF64 {
631 #[inline]
632 fn fetch_add(&self, delta: f64) -> f64 {
633 self.fetch_add(delta)
634 }
635
636 #[inline]
637 fn fetch_sub(&self, delta: f64) -> f64 {
638 self.fetch_sub(delta)
639 }
640
641 #[inline]
642 fn fetch_mul(&self, factor: f64) -> f64 {
643 self.fetch_mul(factor)
644 }
645
646 #[inline]
647 fn fetch_div(&self, divisor: f64) -> f64 {
648 self.fetch_div(divisor)
649 }
650}