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 /// The value observed before the operation completed. Because this
283 /// operation may fail spuriously, a returned value with the same raw bits
284 /// as `current` does not by itself prove that `new` was stored; use
285 /// [`compare_set_weak`](Self::compare_set_weak) when the caller needs an
286 /// explicit success indicator.
287 #[inline]
288 pub fn compare_and_exchange_weak(&self, current: f64, new: f64) -> f64 {
289 match self.inner.compare_exchange_weak(
290 current.to_bits(),
291 new.to_bits(),
292 Ordering::AcqRel,
293 Ordering::Acquire,
294 ) {
295 Ok(prev_bits) => f64::from_bits(prev_bits),
296 Err(actual_bits) => f64::from_bits(actual_bits),
297 }
298 }
299
300 /// Atomically adds a value, returning the old value.
301 ///
302 /// # Memory Ordering
303 ///
304 /// Internally uses a CAS loop with `compare_set_weak`, which uses
305 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
306 /// eventual consistency even under high contention.
307 ///
308 /// # Performance
309 ///
310 /// May be slow in high-contention scenarios due to the CAS loop.
311 /// Consider using atomic integers if performance is critical.
312 ///
313 /// # Parameters
314 ///
315 /// * `delta` - The value to add.
316 ///
317 /// # Returns
318 ///
319 /// The old value before adding.
320 ///
321 /// # Example
322 ///
323 /// ```rust
324 /// use qubit_atomic::Atomic;
325 ///
326 /// let atomic = Atomic::<f64>::new(10.0);
327 /// let old = atomic.fetch_add(5.5);
328 /// assert_eq!(old, 10.0);
329 /// assert_eq!(atomic.load(), 15.5);
330 /// ```
331 #[inline]
332 pub fn fetch_add(&self, delta: f64) -> f64 {
333 self.fetch_update(|current| current + delta)
334 }
335
336 /// Atomically subtracts a value, returning the old value.
337 ///
338 /// # Memory Ordering
339 ///
340 /// Internally uses a CAS loop with `compare_set_weak`, which uses
341 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
342 /// eventual consistency even under high contention.
343 ///
344 /// # Parameters
345 ///
346 /// * `delta` - The value to subtract.
347 ///
348 /// # Returns
349 ///
350 /// The old value before subtracting.
351 ///
352 /// # Example
353 ///
354 /// ```rust
355 /// use qubit_atomic::Atomic;
356 ///
357 /// let atomic = Atomic::<f64>::new(10.0);
358 /// let old = atomic.fetch_sub(3.5);
359 /// assert_eq!(old, 10.0);
360 /// assert_eq!(atomic.load(), 6.5);
361 /// ```
362 #[inline]
363 pub fn fetch_sub(&self, delta: f64) -> f64 {
364 self.fetch_update(|current| current - delta)
365 }
366
367 /// Atomically multiplies by a factor, returning the old value.
368 ///
369 /// # Memory Ordering
370 ///
371 /// Internally uses a CAS loop with `compare_set_weak`, which uses
372 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
373 /// eventual consistency even under high contention.
374 ///
375 /// # Parameters
376 ///
377 /// * `factor` - The factor to multiply by.
378 ///
379 /// # Returns
380 ///
381 /// The old value before multiplying.
382 ///
383 /// # Example
384 ///
385 /// ```rust
386 /// use qubit_atomic::Atomic;
387 ///
388 /// let atomic = Atomic::<f64>::new(10.0);
389 /// let old = atomic.fetch_mul(2.5);
390 /// assert_eq!(old, 10.0);
391 /// assert_eq!(atomic.load(), 25.0);
392 /// ```
393 #[inline]
394 pub fn fetch_mul(&self, factor: f64) -> f64 {
395 self.fetch_update(|current| current * factor)
396 }
397
398 /// Atomically divides by a divisor, returning the old value.
399 ///
400 /// # Memory Ordering
401 ///
402 /// Internally uses a CAS loop with `compare_set_weak`, which uses
403 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
404 /// eventual consistency even under high contention.
405 ///
406 /// # Parameters
407 ///
408 /// * `divisor` - The divisor to divide by.
409 ///
410 /// # Returns
411 ///
412 /// The old value before dividing.
413 ///
414 /// # Example
415 ///
416 /// ```rust
417 /// use qubit_atomic::Atomic;
418 ///
419 /// let atomic = Atomic::<f64>::new(10.0);
420 /// let old = atomic.fetch_div(2.0);
421 /// assert_eq!(old, 10.0);
422 /// assert_eq!(atomic.load(), 5.0);
423 /// ```
424 #[inline]
425 pub fn fetch_div(&self, divisor: f64) -> f64 {
426 self.fetch_update(|current| current / divisor)
427 }
428
429 /// Updates the value using a function, returning the old value.
430 ///
431 /// # Memory Ordering
432 ///
433 /// Internally uses a CAS loop with `compare_set_weak`, which uses
434 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
435 /// eventual consistency even under high contention.
436 ///
437 /// # Parameters
438 ///
439 /// * `f` - A function that takes the current value and returns the new
440 /// value.
441 ///
442 /// # Returns
443 ///
444 /// The old value before the update.
445 ///
446 /// The closure may be called more than once when concurrent updates cause
447 /// CAS retries.
448 #[inline]
449 pub fn fetch_update<F>(&self, f: F) -> f64
450 where
451 F: Fn(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 /// Conditionally updates the value using a function.
464 ///
465 /// Internally uses a CAS loop until the update succeeds or the closure
466 /// rejects the current value by returning `None`.
467 ///
468 /// # Parameters
469 ///
470 /// * `f` - A function that takes the current value and returns the new
471 /// value, or `None` to leave the value unchanged.
472 ///
473 /// # Returns
474 ///
475 /// `Some(old_value)` when the update succeeds, or `None` when `f` rejects
476 /// the observed current value.
477 ///
478 /// The closure may be called more than once when concurrent updates cause
479 /// CAS retries.
480 #[inline]
481 pub fn try_update<F>(&self, f: F) -> Option<f64>
482 where
483 F: Fn(f64) -> Option<f64>,
484 {
485 let mut current = self.load();
486 loop {
487 let new = f(current)?;
488 match self.compare_set_weak(current, new) {
489 Ok(_) => return Some(current),
490 Err(actual) => current = actual,
491 }
492 }
493 }
494
495 /// Gets a reference to the underlying standard library atomic type.
496 ///
497 /// This allows direct access to the standard library's atomic operations
498 /// for advanced use cases that require fine-grained control over memory
499 /// ordering.
500 ///
501 /// # Memory Ordering
502 ///
503 /// When using the returned reference, you have full control over memory
504 /// ordering. Remember to use `f64::to_bits()` and `f64::from_bits()` for
505 /// conversions.
506 ///
507 /// # Returns
508 ///
509 /// A reference to the underlying `std::sync::atomic::AtomicU64`.
510 #[inline]
511 pub fn inner(&self) -> &AtomicU64 {
512 &self.inner
513 }
514}
515
516impl AtomicOps for AtomicF64 {
517 type Value = f64;
518
519 #[inline]
520 fn load(&self) -> f64 {
521 self.load()
522 }
523
524 #[inline]
525 fn store(&self, value: f64) {
526 self.store(value);
527 }
528
529 #[inline]
530 fn swap(&self, value: f64) -> f64 {
531 self.swap(value)
532 }
533
534 #[inline]
535 fn compare_set(&self, current: f64, new: f64) -> Result<(), f64> {
536 self.compare_set(current, new)
537 }
538
539 #[inline]
540 fn compare_set_weak(&self, current: f64, new: f64) -> Result<(), f64> {
541 self.compare_set_weak(current, new)
542 }
543
544 #[inline]
545 fn compare_exchange(&self, current: f64, new: f64) -> f64 {
546 self.compare_and_exchange(current, new)
547 }
548
549 #[inline]
550 fn compare_exchange_weak(&self, current: f64, new: f64) -> f64 {
551 self.compare_and_exchange_weak(current, new)
552 }
553
554 #[inline]
555 fn fetch_update<F>(&self, f: F) -> f64
556 where
557 F: Fn(f64) -> f64,
558 {
559 self.fetch_update(f)
560 }
561
562 #[inline]
563 fn try_update<F>(&self, f: F) -> Option<f64>
564 where
565 F: Fn(f64) -> Option<f64>,
566 {
567 self.try_update(f)
568 }
569}
570
571impl AtomicNumberOps for AtomicF64 {
572 #[inline]
573 fn fetch_add(&self, delta: f64) -> f64 {
574 self.fetch_add(delta)
575 }
576
577 #[inline]
578 fn fetch_sub(&self, delta: f64) -> f64 {
579 self.fetch_sub(delta)
580 }
581
582 #[inline]
583 fn fetch_mul(&self, factor: f64) -> f64 {
584 self.fetch_mul(factor)
585 }
586
587 #[inline]
588 fn fetch_div(&self, divisor: f64) -> f64 {
589 self.fetch_div(divisor)
590 }
591}