Skip to main content

embedded_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-sqrt
9//!
10//! Racine carrée en virgule fixe Q15 pour systèmes embarqués.
11//!
12//! ## Caractéristiques
13//!
14//! - `#![no_std]` — aucune dépendance à la bibliothèque standard
15//! - Arithmétique entière pure (pas de flottants, pas de `libm`)
16//! - Compatible RP2040 (Cortex-M0+) et RP2350 (Cortex-M33)
17//! - Algorithme Newton-Raphson (convergence quadratique, 6 itérations)
18//!
19//! ## Format Q15
20//!
21//! En Q15, un `i32` représente un nombre réel dans `[0.0, 1.0[` :
22//! ```text
23//! valeur_réelle = valeur_i32 / 32768.0
24//! ```
25//! Exemples :
26//! - `0`     → 0.0
27//! - `8192`  → 0.25
28//! - `16384` → 0.5
29//! - `23170` → 0.707 (≈ 1/√2)
30//! - `32767` → ≈ 1.0
31//!
32//! ## Exemple
33//!
34//! ```rust
35//! use embedded_sqrt::sqrt;
36//!
37//! // sqrt(0.25) = 0.5
38//! assert_eq!(sqrt(8192), 16384);
39//!
40//! // sqrt(0.0) = 0.0
41//! assert_eq!(sqrt(0), 0);
42//! ```
43
44#![no_std]
45
46/// Calcule la racine carrée d'un nombre en virgule fixe Q15.
47///
48/// # Arguments
49///
50/// * `a` — valeur en Q15 dans `[0, 32767]`
51///   (`valeur_réelle = a / 32768.0`)
52///
53/// # Retour
54///
55/// `sqrt(a)` en Q15. Retourne `0` si `a <= 0`.
56///
57/// # Précision
58///
59/// Erreur maximale < 150 ULP (unités au dernier rang) sur toute la plage,
60/// soit < 0.005 en valeur réelle.
61///
62/// # Algorithme
63///
64/// Newton-Raphson en arithmétique entière Q15 :
65/// ```text
66/// x_{n+1} = (x_n + a / x_n) / 2
67/// ```
68/// L'entrée est normalisée dans `[0.5, 2.0[` avant l'itération,
69/// puis dénormalisée via `sqrt(a · 4^n) = 2^n · sqrt(a)`.
70///
71/// # Exemples
72///
73/// ```rust
74/// use embedded_sqrt::sqrt;
75///
76/// // sqrt(0.25) = 0.5  →  8192 → 16384
77/// assert!((sqrt(8192) - 16384).abs() < 100);
78///
79/// // sqrt(≈1.0) = ≈1.0  →  32767 → 32767
80/// assert!((sqrt(32767) - 32767).abs() < 150);
81///
82/// // valeurs négatives → 0
83/// assert_eq!(sqrt(-1), 0);
84/// ```
85pub fn sqrt(a: i32) -> i32 {
86    if a <= 0 { return 0; }
87
88    // --- Normalisation ---
89    // On ramène val dans [16384, 65536[ = [0.5, 2.0[ en Q15.
90    // Propriété utilisée : sqrt(a · 4^n) = 2^n · sqrt(a)
91    // On mémorise n dans `shift` pour dénormaliser le résultat.
92    let mut val = a as i64;
93    let mut shift = 0i32;
94    while val < 16384 {
95        val <<= 2;
96        shift += 1;
97    }
98    while val >= 65536 {
99        val >>= 2;
100        shift -= 1;
101    }
102
103    // --- Estimation initiale ---
104    // Deux points de départ couvrant [0.5, 1.0[ et [1.0, 2.0[.
105    // Erreur initiale < 20 %, suffisante pour converger en 6 itérations.
106    let mut x: i64 = if val < 32768 {
107        27525 // ≈ 0.84 en Q15, centre géométrique de [√0.5, 1.0[
108    } else {
109        39000 // ≈ 1.19 en Q15, centre géométrique de [1.0, √2[
110    };
111
112    // --- Itérations Newton-Raphson ---
113    // x_{n+1} = (x_n + val / x_n) / 2
114    //
115    // Division Q15 : (val / x) en Q15 = (val << 15) / x
116    // La convergence est quadratique : les bits corrects doublent à chaque étape.
117    // 6 itérations donnent > 15 bits de précision.
118    for _ in 0..6 {
119        let div = (val << 15) / x;
120        x = (x + div) >> 1;
121    }
122
123    // --- Dénormalisation ---
124    // On annule le shift appliqué à l'entrée.
125    if shift >= 0 {
126        (x >> shift) as i32
127    } else {
128        (x << (-shift)) as i32
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135
136    #[test]
137    fn test_sqrt_perfect_square() {
138        // sqrt(0.25) = 0.5 → sqrt(8192) = 16384 en Q15
139        let res = sqrt(8192);
140        assert!((res - 16384).abs() < 100, "Reçu: {}", res);
141    }
142
143    #[test]
144    fn test_sqrt_one() {
145        // sqrt(≈1.0) = ≈1.0 → sqrt(32767) = 32767 en Q15
146        let res = sqrt(32767);
147        assert!((res - 32767).abs() < 150, "Reçu: {}", res);
148    }
149
150    #[test]
151    fn test_sqrt_various_values() {
152        // sqrt(0.09) = 0.3 → sqrt(2949) = 9830 en Q15
153        let res = sqrt(2949);
154        assert!((res - 9830).abs() < 100, "Reçu: {}", res);
155    }
156
157    #[test]
158    fn test_sqrt_zero_and_negative() {
159        assert_eq!(sqrt(0), 0);
160        assert_eq!(sqrt(-1), 0);
161    }
162}