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}