prism3_atomic/atomic/atomic_f64.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025.
4 * 3-Prism Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9
10//! # Atomic 64-bit Floating Point
11//!
12//! Provides an easy-to-use atomic 64-bit floating point type with sensible
13//! default memory orderings. Implemented using bit conversion with AtomicU64.
14//!
15//! # Author
16//!
17//! Haixing Hu
18
19use std::fmt;
20use std::sync::atomic::AtomicU64;
21use std::sync::atomic::Ordering;
22
23use crate::atomic::traits::Atomic;
24use crate::atomic::traits::AtomicNumber;
25
26/// Atomic 64-bit floating point number.
27///
28/// Provides easy-to-use atomic operations with automatic memory ordering
29/// selection. Implemented using `AtomicU64` with bit conversion.
30///
31/// # Memory Ordering Strategy
32///
33/// This type uses the same memory ordering strategy as atomic integers:
34///
35/// - **Read operations** (`load`): Use `Acquire` ordering to ensure
36/// visibility of prior writes from other threads.
37///
38/// - **Write operations** (`store`): Use `Release` ordering to ensure
39/// visibility of prior writes to other threads.
40///
41/// - **Read-Modify-Write operations** (`swap`, `compare_set`): Use
42/// `AcqRel` ordering for full synchronization.
43///
44/// - **CAS-based arithmetic** (`fetch_add`, `fetch_sub`, etc.): Use
45/// `AcqRel` on success and `Acquire` on failure within the CAS loop.
46/// The loop ensures eventual consistency.
47///
48/// # Implementation Details
49///
50/// Since hardware doesn't provide native atomic floating-point operations,
51/// this type is implemented using `AtomicU64` with `f64::to_bits()` and
52/// `f64::from_bits()` conversions. This preserves bit patterns exactly,
53/// including special values like NaN and infinity.
54///
55/// # Features
56///
57/// - Automatic memory ordering selection
58/// - Arithmetic operations via CAS loops
59/// - Zero-cost abstraction with inline methods
60/// - Access to underlying type via `inner()` for advanced use cases
61///
62/// # Limitations
63///
64/// - Arithmetic operations use CAS loops (slower than integer operations)
65/// - NaN values may cause unexpected behavior in CAS operations
66/// - No max/min operations (complex floating point semantics)
67///
68/// # Example
69///
70/// ```rust
71/// use prism3_rust_concurrent::atomic::AtomicF64;
72///
73/// let atomic = AtomicF64::new(3.14159);
74/// atomic.add(1.0);
75/// assert_eq!(atomic.load(), 4.14159);
76/// ```
77///
78/// # Author
79///
80/// Haixing Hu
81#[repr(transparent)]
82pub struct AtomicF64 {
83 inner: AtomicU64,
84}
85
86impl AtomicF64 {
87 /// Creates a new atomic floating point number.
88 ///
89 /// # Parameters
90 ///
91 /// * `value` - The initial value.
92 ///
93 /// # Example
94 ///
95 /// ```rust
96 /// use prism3_rust_concurrent::atomic::AtomicF64;
97 ///
98 /// let atomic = AtomicF64::new(3.14159);
99 /// assert_eq!(atomic.load(), 3.14159);
100 /// ```
101 #[inline]
102 pub 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 /// # Memory Ordering
166 ///
167 /// - **Success**: Uses `AcqRel` ordering on the underlying `AtomicU64`
168 /// to ensure full synchronization when the exchange succeeds.
169 /// - **Failure**: Uses `Acquire` ordering to observe the actual value
170 /// written by another thread.
171 ///
172 /// # Parameters
173 ///
174 /// * `current` - The expected current value.
175 /// * `new` - The new value to set if current matches.
176 ///
177 /// # Returns
178 ///
179 /// `Ok(())` on success, or `Err(actual)` on failure.
180 #[inline]
181 pub fn compare_set(&self, current: f64, new: f64) -> Result<(), f64> {
182 self.inner
183 .compare_exchange(
184 current.to_bits(),
185 new.to_bits(),
186 Ordering::AcqRel,
187 Ordering::Acquire,
188 )
189 .map(|_| ())
190 .map_err(f64::from_bits)
191 }
192
193 /// Weak version of compare-and-set.
194 ///
195 /// May spuriously fail even when the comparison succeeds. Should be used
196 /// in a loop.
197 ///
198 /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
199 ///
200 /// # Parameters
201 ///
202 /// * `current` - The expected current value.
203 /// * `new` - The new value to set if current matches.
204 ///
205 /// # Returns
206 ///
207 /// `Ok(())` on success, or `Err(actual)` on failure.
208 #[inline]
209 pub fn compare_set_weak(&self, current: f64, new: f64) -> Result<(), f64> {
210 self.inner
211 .compare_exchange_weak(
212 current.to_bits(),
213 new.to_bits(),
214 Ordering::AcqRel,
215 Ordering::Acquire,
216 )
217 .map(|_| ())
218 .map_err(f64::from_bits)
219 }
220
221 /// Compares and exchanges the value atomically, returning the previous
222 /// value.
223 ///
224 /// If the current value equals `current`, sets it to `new` and returns
225 /// the old value. Otherwise, returns the actual current value.
226 ///
227 /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
228 ///
229 /// # Parameters
230 ///
231 /// * `current` - The expected current value.
232 /// * `new` - The new value to set if current matches.
233 ///
234 /// # Returns
235 ///
236 /// The value before the operation.
237 #[inline]
238 pub fn compare_and_exchange(&self, current: f64, new: f64) -> f64 {
239 match self.inner.compare_exchange(
240 current.to_bits(),
241 new.to_bits(),
242 Ordering::AcqRel,
243 Ordering::Acquire,
244 ) {
245 Ok(prev_bits) => f64::from_bits(prev_bits),
246 Err(actual_bits) => f64::from_bits(actual_bits),
247 }
248 }
249
250 /// Weak version of compare-and-exchange.
251 ///
252 /// May spuriously fail even when the comparison succeeds. Should be used
253 /// in a loop.
254 ///
255 /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
256 ///
257 /// # Parameters
258 ///
259 /// * `current` - The expected current value.
260 /// * `new` - The new value to set if current matches.
261 ///
262 /// # Returns
263 ///
264 /// The value before the operation.
265 #[inline]
266 pub fn compare_and_exchange_weak(&self, current: f64, new: f64) -> f64 {
267 match self.inner.compare_exchange_weak(
268 current.to_bits(),
269 new.to_bits(),
270 Ordering::AcqRel,
271 Ordering::Acquire,
272 ) {
273 Ok(prev_bits) => f64::from_bits(prev_bits),
274 Err(actual_bits) => f64::from_bits(actual_bits),
275 }
276 }
277
278 /// Atomically adds a value, returning the old value.
279 ///
280 /// # Memory Ordering
281 ///
282 /// Internally uses a CAS loop with `compare_set_weak`, which uses
283 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
284 /// eventual consistency even under high contention.
285 ///
286 /// # Performance
287 ///
288 /// May be slow in high-contention scenarios due to the CAS loop.
289 /// Consider using atomic integers if performance is critical.
290 ///
291 /// # Parameters
292 ///
293 /// * `delta` - The value to add.
294 ///
295 /// # Returns
296 ///
297 /// The old value before adding.
298 ///
299 /// # Example
300 ///
301 /// ```rust
302 /// use prism3_rust_concurrent::atomic::AtomicF64;
303 ///
304 /// let atomic = AtomicF64::new(10.0);
305 /// let old = atomic.fetch_add(5.5);
306 /// assert_eq!(old, 10.0);
307 /// assert_eq!(atomic.load(), 15.5);
308 /// ```
309 #[inline]
310 pub fn fetch_add(&self, delta: f64) -> f64 {
311 let mut current = self.load();
312 loop {
313 let new = current + delta;
314 match self.compare_set_weak(current, new) {
315 Ok(_) => return current,
316 Err(actual) => current = actual,
317 }
318 }
319 }
320
321 /// Atomically subtracts a value, returning the old value.
322 ///
323 /// # Memory Ordering
324 ///
325 /// Internally uses a CAS loop with `compare_set_weak`, which uses
326 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
327 /// eventual consistency even under high contention.
328 ///
329 /// # Parameters
330 ///
331 /// * `delta` - The value to subtract.
332 ///
333 /// # Returns
334 ///
335 /// The old value before subtracting.
336 ///
337 /// # Example
338 ///
339 /// ```rust
340 /// use prism3_rust_concurrent::atomic::AtomicF64;
341 ///
342 /// let atomic = AtomicF64::new(10.0);
343 /// let old = atomic.fetch_sub(3.5);
344 /// assert_eq!(old, 10.0);
345 /// assert_eq!(atomic.load(), 6.5);
346 /// ```
347 #[inline]
348 pub fn fetch_sub(&self, delta: f64) -> f64 {
349 let mut current = self.load();
350 loop {
351 let new = current - delta;
352 match self.compare_set_weak(current, new) {
353 Ok(_) => return current,
354 Err(actual) => current = actual,
355 }
356 }
357 }
358
359 /// Atomically multiplies by a factor, returning the old value.
360 ///
361 /// # Memory Ordering
362 ///
363 /// Internally uses a CAS loop with `compare_set_weak`, which uses
364 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
365 /// eventual consistency even under high contention.
366 ///
367 /// # Parameters
368 ///
369 /// * `factor` - The factor to multiply by.
370 ///
371 /// # Returns
372 ///
373 /// The old value before multiplying.
374 ///
375 /// # Example
376 ///
377 /// ```rust
378 /// use prism3_rust_concurrent::atomic::AtomicF64;
379 ///
380 /// let atomic = AtomicF64::new(10.0);
381 /// let old = atomic.fetch_mul(2.5);
382 /// assert_eq!(old, 10.0);
383 /// assert_eq!(atomic.load(), 25.0);
384 /// ```
385 #[inline]
386 pub fn fetch_mul(&self, factor: f64) -> f64 {
387 let mut current = self.load();
388 loop {
389 let new = current * factor;
390 match self.compare_set_weak(current, new) {
391 Ok(_) => return current,
392 Err(actual) => current = actual,
393 }
394 }
395 }
396
397 /// Atomically divides by a divisor, returning the old value.
398 ///
399 /// # Memory Ordering
400 ///
401 /// Internally uses a CAS loop with `compare_set_weak`, which uses
402 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
403 /// eventual consistency even under high contention.
404 ///
405 /// # Parameters
406 ///
407 /// * `divisor` - The divisor to divide by.
408 ///
409 /// # Returns
410 ///
411 /// The old value before dividing.
412 ///
413 /// # Example
414 ///
415 /// ```rust
416 /// use prism3_rust_concurrent::atomic::AtomicF64;
417 ///
418 /// let atomic = AtomicF64::new(10.0);
419 /// let old = atomic.fetch_div(2.0);
420 /// assert_eq!(old, 10.0);
421 /// assert_eq!(atomic.load(), 5.0);
422 /// ```
423 #[inline]
424 pub fn fetch_div(&self, divisor: f64) -> f64 {
425 let mut current = self.load();
426 loop {
427 let new = current / divisor;
428 match self.compare_set_weak(current, new) {
429 Ok(_) => return current,
430 Err(actual) => current = actual,
431 }
432 }
433 }
434
435 /// Updates the value using a function, returning the old value.
436 ///
437 /// # Memory Ordering
438 ///
439 /// Internally uses a CAS loop with `compare_set_weak`, which uses
440 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
441 /// eventual consistency even under high contention.
442 ///
443 /// # Parameters
444 ///
445 /// * `f` - A function that takes the current value and returns the new
446 /// value.
447 ///
448 /// # Returns
449 ///
450 /// The old value before the update.
451 #[inline]
452 pub fn fetch_update<F>(&self, f: F) -> f64
453 where
454 F: Fn(f64) -> f64,
455 {
456 let mut current = self.load();
457 loop {
458 let new = f(current);
459 match self.compare_set_weak(current, new) {
460 Ok(_) => return current,
461 Err(actual) => current = actual,
462 }
463 }
464 }
465
466 /// Gets a reference to the underlying standard library atomic type.
467 ///
468 /// This allows direct access to the standard library's atomic operations
469 /// for advanced use cases that require fine-grained control over memory
470 /// ordering.
471 ///
472 /// # Memory Ordering
473 ///
474 /// When using the returned reference, you have full control over memory
475 /// ordering. Remember to use `f64::to_bits()` and `f64::from_bits()` for
476 /// conversions.
477 ///
478 /// # Returns
479 ///
480 /// A reference to the underlying `std::sync::atomic::AtomicU64`.
481 #[inline]
482 pub fn inner(&self) -> &AtomicU64 {
483 &self.inner
484 }
485}
486
487impl Atomic for AtomicF64 {
488 type Value = f64;
489
490 #[inline]
491 fn load(&self) -> f64 {
492 self.load()
493 }
494
495 #[inline]
496 fn store(&self, value: f64) {
497 self.store(value);
498 }
499
500 #[inline]
501 fn swap(&self, value: f64) -> f64 {
502 self.swap(value)
503 }
504
505 #[inline]
506 fn compare_set(&self, current: f64, new: f64) -> Result<(), f64> {
507 self.compare_set(current, new)
508 }
509
510 #[inline]
511 fn compare_set_weak(&self, current: f64, new: f64) -> Result<(), f64> {
512 self.compare_set_weak(current, new)
513 }
514
515 #[inline]
516 fn compare_exchange(&self, current: f64, new: f64) -> f64 {
517 self.compare_and_exchange(current, new)
518 }
519
520 #[inline]
521 fn compare_exchange_weak(&self, current: f64, new: f64) -> f64 {
522 self.compare_and_exchange_weak(current, new)
523 }
524
525 #[inline]
526 fn fetch_update<F>(&self, f: F) -> f64
527 where
528 F: Fn(f64) -> f64,
529 {
530 self.fetch_update(f)
531 }
532}
533
534impl AtomicNumber for AtomicF64 {
535 #[inline]
536 fn fetch_add(&self, delta: f64) -> f64 {
537 self.fetch_add(delta)
538 }
539
540 #[inline]
541 fn fetch_sub(&self, delta: f64) -> f64 {
542 self.fetch_sub(delta)
543 }
544
545 #[inline]
546 fn fetch_mul(&self, factor: f64) -> f64 {
547 self.fetch_mul(factor)
548 }
549
550 #[inline]
551 fn fetch_div(&self, divisor: f64) -> f64 {
552 self.fetch_div(divisor)
553 }
554}
555
556unsafe impl Send for AtomicF64 {}
557unsafe impl Sync for AtomicF64 {}
558
559impl Default for AtomicF64 {
560 #[inline]
561 fn default() -> Self {
562 Self::new(0.0)
563 }
564}
565
566impl From<f64> for AtomicF64 {
567 #[inline]
568 fn from(value: f64) -> Self {
569 Self::new(value)
570 }
571}
572
573impl fmt::Debug for AtomicF64 {
574 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
575 f.debug_struct("AtomicF64")
576 .field("value", &self.load())
577 .finish()
578 }
579}
580
581impl fmt::Display for AtomicF64 {
582 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
583 write!(f, "{}", self.load())
584 }
585}