prism3_atomic/atomic/atomic_f32.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025.
4 * 3-Prism Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9
10//! # Atomic 32-bit Floating Point
11//!
12//! Provides an easy-to-use atomic 32-bit floating point type with sensible
13//! default memory orderings. Implemented using bit conversion with AtomicU32.
14//!
15//! # Author
16//!
17//! Haixing Hu
18
19use std::fmt;
20use std::sync::atomic::AtomicU32;
21use std::sync::atomic::Ordering;
22
23use crate::atomic::traits::Atomic;
24use crate::atomic::traits::AtomicNumber;
25
26/// Atomic 32-bit floating point number.
27///
28/// Provides easy-to-use atomic operations with automatic memory ordering
29/// selection. Implemented using `AtomicU32` 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 `AtomicU32` with `f32::to_bits()` and
52/// `f32::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::AtomicF32;
72/// use std::sync::Arc;
73/// use std::thread;
74///
75/// let sum = Arc::new(AtomicF32::new(0.0));
76/// let mut handles = vec![];
77///
78/// for _ in 0..10 {
79/// let sum = sum.clone();
80/// let handle = thread::spawn(move || {
81/// for _ in 0..100 {
82/// sum.add(0.1);
83/// }
84/// });
85/// handles.push(handle);
86/// }
87///
88/// for handle in handles {
89/// handle.join().unwrap();
90/// }
91///
92/// // Note: Due to floating point precision, result may not be exactly 100.0
93/// let result = sum.load();
94/// assert!((result - 100.0).abs() < 0.01);
95/// ```
96///
97/// # Author
98///
99/// Haixing Hu
100#[repr(transparent)]
101pub struct AtomicF32 {
102 inner: AtomicU32,
103}
104
105impl AtomicF32 {
106 /// Creates a new atomic floating point number.
107 ///
108 /// # Parameters
109 ///
110 /// * `value` - The initial value.
111 ///
112 /// # Example
113 ///
114 /// ```rust
115 /// use prism3_rust_concurrent::atomic::AtomicF32;
116 ///
117 /// let atomic = AtomicF32::new(3.14);
118 /// assert_eq!(atomic.load(), 3.14);
119 /// ```
120 #[inline]
121 pub fn new(value: f32) -> Self {
122 Self {
123 inner: AtomicU32::new(value.to_bits()),
124 }
125 }
126
127 /// Gets the current value.
128 ///
129 /// # Memory Ordering
130 ///
131 /// Uses `Acquire` ordering on the underlying `AtomicU32`. This ensures
132 /// that all writes from other threads that happened before a `Release`
133 /// store are visible after this load.
134 ///
135 /// # Returns
136 ///
137 /// The current value.
138 ///
139 /// # Example
140 ///
141 /// ```rust
142 /// use prism3_rust_concurrent::atomic::AtomicF32;
143 ///
144 /// let atomic = AtomicF32::new(3.14);
145 /// assert_eq!(atomic.load(), 3.14);
146 /// ```
147 #[inline]
148 pub fn load(&self) -> f32 {
149 f32::from_bits(self.inner.load(Ordering::Acquire))
150 }
151
152 /// Sets a new value.
153 ///
154 /// # Memory Ordering
155 ///
156 /// Uses `Release` ordering on the underlying `AtomicU32`. This ensures
157 /// that all prior writes in this thread are visible to other threads
158 /// that perform an `Acquire` load.
159 ///
160 /// # Parameters
161 ///
162 /// * `value` - The new value to set.
163 ///
164 /// # Example
165 ///
166 /// ```rust
167 /// use prism3_rust_concurrent::atomic::AtomicF32;
168 ///
169 /// let atomic = AtomicF32::new(0.0);
170 /// atomic.store(3.14);
171 /// assert_eq!(atomic.load(), 3.14);
172 /// ```
173 #[inline]
174 pub fn store(&self, value: f32) {
175 self.inner.store(value.to_bits(), Ordering::Release);
176 }
177
178 /// Swaps the current value with a new value, returning the old value.
179 ///
180 /// # Memory Ordering
181 ///
182 /// Uses `AcqRel` ordering on the underlying `AtomicU32`. This provides
183 /// full synchronization for this read-modify-write operation.
184 ///
185 /// # Parameters
186 ///
187 /// * `value` - The new value to swap in.
188 ///
189 /// # Returns
190 ///
191 /// The old value.
192 ///
193 /// # Example
194 ///
195 /// ```rust
196 /// use prism3_rust_concurrent::atomic::AtomicF32;
197 ///
198 /// let atomic = AtomicF32::new(1.0);
199 /// let old = atomic.swap(2.0);
200 /// assert_eq!(old, 1.0);
201 /// assert_eq!(atomic.load(), 2.0);
202 /// ```
203 #[inline]
204 pub fn swap(&self, value: f32) -> f32 {
205 f32::from_bits(self.inner.swap(value.to_bits(), Ordering::AcqRel))
206 }
207
208 /// Compares and sets the value atomically.
209 ///
210 /// If the current value equals `current`, sets it to `new` and returns
211 /// `Ok(())`. Otherwise, returns `Err(actual)` where `actual` is the
212 /// current value.
213 ///
214 /// # Memory Ordering
215 ///
216 /// - **Success**: Uses `AcqRel` ordering on the underlying `AtomicU32`
217 /// to ensure full synchronization when the exchange succeeds.
218 /// - **Failure**: Uses `Acquire` ordering to observe the actual value
219 /// written by another thread.
220 ///
221 /// # Parameters
222 ///
223 /// * `current` - The expected current value.
224 /// * `new` - The new value to set if current matches.
225 ///
226 /// # Returns
227 ///
228 /// `Ok(())` on success, or `Err(actual)` on failure.
229 ///
230 /// # Warning
231 ///
232 /// Due to NaN != NaN, CAS operations with NaN values may behave
233 /// unexpectedly. Avoid using NaN in atomic floating point operations.
234 ///
235 /// # Example
236 ///
237 /// ```rust
238 /// use prism3_rust_concurrent::atomic::AtomicF32;
239 ///
240 /// let atomic = AtomicF32::new(1.0);
241 /// assert!(atomic.compare_set(1.0, 2.0).is_ok());
242 /// assert_eq!(atomic.load(), 2.0);
243 /// ```
244 #[inline]
245 pub fn compare_set(&self, current: f32, new: f32) -> Result<(), f32> {
246 self.inner
247 .compare_exchange(
248 current.to_bits(),
249 new.to_bits(),
250 Ordering::AcqRel,
251 Ordering::Acquire,
252 )
253 .map(|_| ())
254 .map_err(f32::from_bits)
255 }
256
257 /// Weak version of compare-and-set.
258 ///
259 /// May spuriously fail even when the comparison succeeds. Should be used
260 /// in a loop.
261 ///
262 /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
263 ///
264 /// # Parameters
265 ///
266 /// * `current` - The expected current value.
267 /// * `new` - The new value to set if current matches.
268 ///
269 /// # Returns
270 ///
271 /// `Ok(())` on success, or `Err(actual)` on failure.
272 ///
273 /// # Example
274 ///
275 /// ```rust
276 /// use prism3_rust_concurrent::atomic::AtomicF32;
277 ///
278 /// let atomic = AtomicF32::new(1.0);
279 /// let mut current = atomic.load();
280 /// loop {
281 /// match atomic.compare_set_weak(current, current + 1.0) {
282 /// Ok(_) => break,
283 /// Err(actual) => current = actual,
284 /// }
285 /// }
286 /// assert_eq!(atomic.load(), 2.0);
287 /// ```
288 #[inline]
289 pub fn compare_set_weak(&self, current: f32, new: f32) -> Result<(), f32> {
290 self.inner
291 .compare_exchange_weak(
292 current.to_bits(),
293 new.to_bits(),
294 Ordering::AcqRel,
295 Ordering::Acquire,
296 )
297 .map(|_| ())
298 .map_err(f32::from_bits)
299 }
300
301 /// Compares and exchanges the value atomically, returning the previous
302 /// value.
303 ///
304 /// If the current value equals `current`, sets it to `new` and returns
305 /// the old value. Otherwise, returns the actual current value.
306 ///
307 /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
308 ///
309 /// # Parameters
310 ///
311 /// * `current` - The expected current value.
312 /// * `new` - The new value to set if current matches.
313 ///
314 /// # Returns
315 ///
316 /// The value before the operation.
317 ///
318 /// # Example
319 ///
320 /// ```rust
321 /// use prism3_rust_concurrent::atomic::AtomicF32;
322 ///
323 /// let atomic = AtomicF32::new(1.0);
324 /// let prev = atomic.compare_and_exchange(1.0, 2.0);
325 /// assert_eq!(prev, 1.0);
326 /// assert_eq!(atomic.load(), 2.0);
327 /// ```
328 #[inline]
329 pub fn compare_and_exchange(&self, current: f32, new: f32) -> f32 {
330 match self.inner.compare_exchange(
331 current.to_bits(),
332 new.to_bits(),
333 Ordering::AcqRel,
334 Ordering::Acquire,
335 ) {
336 Ok(prev_bits) => f32::from_bits(prev_bits),
337 Err(actual_bits) => f32::from_bits(actual_bits),
338 }
339 }
340
341 /// Weak version of compare-and-exchange.
342 ///
343 /// May spuriously fail even when the comparison succeeds. Should be used
344 /// in a loop.
345 ///
346 /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
347 ///
348 /// # Parameters
349 ///
350 /// * `current` - The expected current value.
351 /// * `new` - The new value to set if current matches.
352 ///
353 /// # Returns
354 ///
355 /// The value before the operation.
356 ///
357 /// # Example
358 ///
359 /// ```rust
360 /// use prism3_rust_concurrent::atomic::AtomicF32;
361 ///
362 /// let atomic = AtomicF32::new(1.0);
363 /// let mut current = atomic.load();
364 /// loop {
365 /// let prev = atomic.compare_and_exchange_weak(current, current + 1.0);
366 /// if prev == current {
367 /// break;
368 /// }
369 /// current = prev;
370 /// }
371 /// assert_eq!(atomic.load(), 2.0);
372 /// ```
373 #[inline]
374 pub fn compare_and_exchange_weak(&self, current: f32, new: f32) -> f32 {
375 match self.inner.compare_exchange_weak(
376 current.to_bits(),
377 new.to_bits(),
378 Ordering::AcqRel,
379 Ordering::Acquire,
380 ) {
381 Ok(prev_bits) => f32::from_bits(prev_bits),
382 Err(actual_bits) => f32::from_bits(actual_bits),
383 }
384 }
385
386 /// Atomically adds a value, returning the old value.
387 ///
388 /// # Memory Ordering
389 ///
390 /// Internally uses a CAS loop with `compare_set_weak`, which uses
391 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
392 /// eventual consistency even under high contention.
393 ///
394 /// # Performance
395 ///
396 /// May be slow in high-contention scenarios due to the CAS loop.
397 /// Consider using atomic integers if performance is critical.
398 ///
399 /// # Parameters
400 ///
401 /// * `delta` - The value to add.
402 ///
403 /// # Returns
404 ///
405 /// The old value before adding.
406 ///
407 /// # Example
408 ///
409 /// ```rust
410 /// use prism3_rust_concurrent::atomic::AtomicF32;
411 ///
412 /// let atomic = AtomicF32::new(10.0);
413 /// let old = atomic.fetch_add(5.5);
414 /// assert_eq!(old, 10.0);
415 /// assert_eq!(atomic.load(), 15.5);
416 /// ```
417 #[inline]
418 pub fn fetch_add(&self, delta: f32) -> f32 {
419 let mut current = self.load();
420 loop {
421 let new = current + delta;
422 match self.compare_set_weak(current, new) {
423 Ok(_) => return current,
424 Err(actual) => current = actual,
425 }
426 }
427 }
428
429 /// Atomically subtracts a value, 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 /// * `delta` - The value to subtract.
440 ///
441 /// # Returns
442 ///
443 /// The old value before subtracting.
444 ///
445 /// # Example
446 ///
447 /// ```rust
448 /// use prism3_rust_concurrent::atomic::AtomicF32;
449 ///
450 /// let atomic = AtomicF32::new(10.0);
451 /// let old = atomic.fetch_sub(3.5);
452 /// assert_eq!(old, 10.0);
453 /// assert_eq!(atomic.load(), 6.5);
454 /// ```
455 #[inline]
456 pub fn fetch_sub(&self, delta: f32) -> f32 {
457 let mut current = self.load();
458 loop {
459 let new = current - delta;
460 match self.compare_set_weak(current, new) {
461 Ok(_) => return current,
462 Err(actual) => current = actual,
463 }
464 }
465 }
466
467 /// Atomically multiplies by a factor, returning the old value.
468 ///
469 /// # Memory Ordering
470 ///
471 /// Internally uses a CAS loop with `compare_set_weak`, which uses
472 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
473 /// eventual consistency even under high contention.
474 ///
475 /// # Parameters
476 ///
477 /// * `factor` - The factor to multiply by.
478 ///
479 /// # Returns
480 ///
481 /// The old value before multiplying.
482 ///
483 /// # Example
484 ///
485 /// ```rust
486 /// use prism3_rust_concurrent::atomic::AtomicF32;
487 ///
488 /// let atomic = AtomicF32::new(10.0);
489 /// let old = atomic.fetch_mul(2.5);
490 /// assert_eq!(old, 10.0);
491 /// assert_eq!(atomic.load(), 25.0);
492 /// ```
493 #[inline]
494 pub fn fetch_mul(&self, factor: f32) -> f32 {
495 let mut current = self.load();
496 loop {
497 let new = current * factor;
498 match self.compare_set_weak(current, new) {
499 Ok(_) => return current,
500 Err(actual) => current = actual,
501 }
502 }
503 }
504
505 /// Atomically divides by a divisor, returning the old value.
506 ///
507 /// # Memory Ordering
508 ///
509 /// Internally uses a CAS loop with `compare_set_weak`, which uses
510 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
511 /// eventual consistency even under high contention.
512 ///
513 /// # Parameters
514 ///
515 /// * `divisor` - The divisor to divide by.
516 ///
517 /// # Returns
518 ///
519 /// The old value before dividing.
520 ///
521 /// # Example
522 ///
523 /// ```rust
524 /// use prism3_rust_concurrent::atomic::AtomicF32;
525 ///
526 /// let atomic = AtomicF32::new(10.0);
527 /// let old = atomic.fetch_div(2.0);
528 /// assert_eq!(old, 10.0);
529 /// assert_eq!(atomic.load(), 5.0);
530 /// ```
531 #[inline]
532 pub fn fetch_div(&self, divisor: f32) -> f32 {
533 let mut current = self.load();
534 loop {
535 let new = current / divisor;
536 match self.compare_set_weak(current, new) {
537 Ok(_) => return current,
538 Err(actual) => current = actual,
539 }
540 }
541 }
542
543 /// Updates the value using a function, returning the old value.
544 ///
545 /// # Memory Ordering
546 ///
547 /// Internally uses a CAS loop with `compare_set_weak`, which uses
548 /// `AcqRel` on success and `Acquire` on failure. The loop ensures
549 /// eventual consistency even under high contention.
550 ///
551 /// # Parameters
552 ///
553 /// * `f` - A function that takes the current value and returns the new
554 /// value.
555 ///
556 /// # Returns
557 ///
558 /// The old value before the update.
559 ///
560 /// # Example
561 ///
562 /// ```rust
563 /// use prism3_rust_concurrent::atomic::AtomicF32;
564 ///
565 /// let atomic = AtomicF32::new(10.0);
566 /// let old = atomic.fetch_update(|x| x * 2.0);
567 /// assert_eq!(old, 10.0);
568 /// assert_eq!(atomic.load(), 20.0);
569 /// ```
570 #[inline]
571 pub fn fetch_update<F>(&self, f: F) -> f32
572 where
573 F: Fn(f32) -> f32,
574 {
575 let mut current = self.load();
576 loop {
577 let new = f(current);
578 match self.compare_set_weak(current, new) {
579 Ok(_) => return current,
580 Err(actual) => current = actual,
581 }
582 }
583 }
584
585 /// Gets a reference to the underlying standard library atomic type.
586 ///
587 /// This allows direct access to the standard library's atomic operations
588 /// for advanced use cases that require fine-grained control over memory
589 /// ordering.
590 ///
591 /// # Memory Ordering
592 ///
593 /// When using the returned reference, you have full control over memory
594 /// ordering. Remember to use `f32::to_bits()` and `f32::from_bits()` for
595 /// conversions.
596 ///
597 /// # Returns
598 ///
599 /// A reference to the underlying `std::sync::atomic::AtomicU32`.
600 ///
601 /// # Example
602 ///
603 /// ```rust
604 /// use prism3_rust_concurrent::atomic::AtomicF32;
605 /// use std::sync::atomic::Ordering;
606 ///
607 /// let atomic = AtomicF32::new(0.0);
608 /// atomic.inner().store(3.14_f32.to_bits(), Ordering::Relaxed);
609 /// let bits = atomic.inner().load(Ordering::Relaxed);
610 /// assert_eq!(f32::from_bits(bits), 3.14);
611 /// ```
612 #[inline]
613 pub fn inner(&self) -> &AtomicU32 {
614 &self.inner
615 }
616}
617
618impl Atomic for AtomicF32 {
619 type Value = f32;
620
621 #[inline]
622 fn load(&self) -> f32 {
623 self.load()
624 }
625
626 #[inline]
627 fn store(&self, value: f32) {
628 self.store(value);
629 }
630
631 #[inline]
632 fn swap(&self, value: f32) -> f32 {
633 self.swap(value)
634 }
635
636 #[inline]
637 fn compare_set(&self, current: f32, new: f32) -> Result<(), f32> {
638 self.compare_set(current, new)
639 }
640
641 #[inline]
642 fn compare_set_weak(&self, current: f32, new: f32) -> Result<(), f32> {
643 self.compare_set_weak(current, new)
644 }
645
646 #[inline]
647 fn compare_exchange(&self, current: f32, new: f32) -> f32 {
648 self.compare_and_exchange(current, new)
649 }
650
651 #[inline]
652 fn compare_exchange_weak(&self, current: f32, new: f32) -> f32 {
653 self.compare_and_exchange_weak(current, new)
654 }
655
656 #[inline]
657 fn fetch_update<F>(&self, f: F) -> f32
658 where
659 F: Fn(f32) -> f32,
660 {
661 self.fetch_update(f)
662 }
663}
664
665impl AtomicNumber for AtomicF32 {
666 #[inline]
667 fn fetch_add(&self, delta: f32) -> f32 {
668 self.fetch_add(delta)
669 }
670
671 #[inline]
672 fn fetch_sub(&self, delta: f32) -> f32 {
673 self.fetch_sub(delta)
674 }
675
676 #[inline]
677 fn fetch_mul(&self, factor: f32) -> f32 {
678 self.fetch_mul(factor)
679 }
680
681 #[inline]
682 fn fetch_div(&self, divisor: f32) -> f32 {
683 self.fetch_div(divisor)
684 }
685}
686
687unsafe impl Send for AtomicF32 {}
688unsafe impl Sync for AtomicF32 {}
689
690impl Default for AtomicF32 {
691 #[inline]
692 fn default() -> Self {
693 Self::new(0.0)
694 }
695}
696
697impl From<f32> for AtomicF32 {
698 #[inline]
699 fn from(value: f32) -> Self {
700 Self::new(value)
701 }
702}
703
704impl fmt::Debug for AtomicF32 {
705 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
706 f.debug_struct("AtomicF32")
707 .field("value", &self.load())
708 .finish()
709 }
710}
711
712impl fmt::Display for AtomicF32 {
713 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
714 write!(f, "{}", self.load())
715 }
716}