embedded_f32_sqrt/lib.rs
1// Copyright (C) 2026 Jorge Andre Castro
2//
3// Ce programme est un logiciel libre : vous pouvez le redistribuer et/ou le modifier
4// selon les termes de la Licence Publique Générale GNU telle que publiée par la
5// Free Software Foundation, soit la version 2 de la licence, soit (à votre convention)
6// n'importe quelle version ultérieure.
7//
8//! # embedded-f32-sqrt
9//!
10//! Racine carrée `f32` par Newton-Raphson pour systèmes embarqués `no_std`.
11//!
12//! Sans dépendance, sans `unsafe`, sans FPU requise.
13//!
14//! ```rust
15//! use embedded_f32_sqrt::sqrt;
16//!
17//! assert!((sqrt(9.0).unwrap() - 3.0).abs() < 1e-5);
18//! assert!((sqrt(2.0).unwrap() - 1.414_213_5).abs() < 1e-6);
19//! assert!(sqrt(-1.0).is_err());
20//! ```
21
22#![no_std]
23#![forbid(unsafe_code)]
24#![warn(missing_docs)]
25
26/// Erreur retournée pour une entrée négative.
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct NegativeInput;
29
30/// Racine carrée `f32` par Newton-Raphson.
31///
32/// Estimation initiale via décalage de bits IEEE 754,
33/// puis 5 itérations. Précision : erreur relative < 1 ULP f32.
34///
35/// Retourne `Err(NegativeInput)` si `x < 0.0`.
36pub fn sqrt(x: f32) -> Result<f32, NegativeInput> {
37 if x.is_nan() {
38 return Ok(f32::NAN);
39 }
40
41 if x.is_infinite() {
42 return Ok(f32::INFINITY);
43 }
44
45 if x < 0.0 {
46 return Err(NegativeInput);
47 }
48
49 if x == 0.0 {
50 return Ok(0.0);
51 }
52 // Estimation initiale par manipulation d'exposant IEEE 754
53 let mut r = f32::from_bits(((x.to_bits() >> 1) + 0x1FBB_4F2E) & 0x7FFF_FFFF);
54
55 // Newton-Raphson : r = (r + x/r) / 2
56 for _ in 0..5 {
57 r = 0.5 * (r + x / r);
58 }
59
60 Ok(r)
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn perfect_squares() {
69 for n in [0u32, 1, 4, 9, 16, 25, 100, 10_000] {
70 let expected = (n as f32).sqrt();
71 assert!((sqrt(n as f32).unwrap() - expected).abs() < 1e-4);
72 }
73 }
74
75 #[test]
76 fn irrational() {
77 assert!((sqrt(2.0).unwrap() - 1.414_213_5_f32).abs() < 1e-5);
78 }
79
80 #[test]
81 fn zero() {
82 assert_eq!(sqrt(0.0), Ok(0.0));
83 }
84
85 #[test]
86 fn negative() {
87 assert_eq!(sqrt(-1.0), Err(NegativeInput));
88 }
89}