Your Browser Does Math Differently on Every OS, and Anti-Bot Systems Read the Bits
Article d'ingénierie publié le **12 juillet 2026** par **Scrapfly Engineering**, sur un canal de *fingerprinting* de navigateur peu connu : **les derniers bits d'un nombre flottant trahissent le système d'exploitation**. **Le mécanisme** : IEEE 754 définit comment un `double` est stocké, mais **n'exige pas** que `sin`, `cos`, `tanh` ou `exp` soient correctement arrondis ; chaque système livre donc une **libm** qui échange une fraction d'ULP contre de la vitesse, avec ses propres coefficients minimax, tables et constantes de réduction. Résultat, `Math.tanh(0.8)` rend **trois valeurs différentes** selon glibc (Linux), libsystem_m (macOS) et UCRT (Windows) — *« one tanh call on the right input is a per-OS signature. Claim macOS, return Linux math bits, and you have contradicted your own User-Agent. »* **Le tell est récent et daté précisément** : jusqu'à **Chrome 147**, V8 calculait `tanh` avec un portage **fdlibm** embarqué, identique partout et ne fuitant rien ; le commit V8 `c1486295ae5` l'a remplacé par `std::tanh`, livré dans V8 14.8.57 soit **Chrome 148** — 148, 149 et 150 fuient, 147 et antérieurs non. **Trois surfaces concentrent les fuites** : `Math.tanh` (le **seul** `Math.*` concerné, puisque V8 embarque et lie statiquement le reste), **toutes les fonctions trigonométriques CSS** (Blink appelle la libm hôte directement, après une réduction d'angle en degrés qui ne partage pas le code de `Math.sin`), et **Web Audio** (où le compresseur reste en scalaire libsystem_m tandis que la FFT et les étages vectoriels passent par **Accelerate**). **Quatre pièges** rendent la contre-mesure difficile : seule une partie des fonctions fuit — donc **spoofer les autres crée une incohérence détectable** ; JavaScript et CSS sont des chemins de code distincts ; **macOS embarque deux bibliothèques mathématiques qui divergent entre elles** (scalaire vs Accelerate, de 10 à 89 % des entrées selon la fonction : `cos(0)` rend `1.0` d'un côté, `0.9999999999999999` de l'autre) ; et l'**architecture fuit aussi** (FMA et propagation du signe des NaN diffèrent entre ARM et x86). **La parade rejetée et la parade retenue** : ajouter du bruit échoue deux fois — la valeur ne correspond à **aucun** OS réel, et la non-déterminisme par appel est lui-même un tell. La seule voie est la **reproduction bit à bit** : extraire les coefficients de la libm cible, les transcrire **en hexadécimal** (une transcription décimale arrondirait autrement), écrire chaque fusion multiplication-addition en `fma()` explicite et compiler avec `-ffp-contract=off` pour que le compilateur n'en invente ni n'en supprime aucune. **Divulgation à consigner** : l'éditeur indique en tête que *« the posts here are drafted with AI »*, les mécanismes, chiffres et code restant les siens.
**Scrapfly Engineering** — équipe d'ingénierie de **Scrapfly** · fournisseur d'infrastructure de collecte web. Le texte annonce sa position d'intérêt sans détour : *« Scrapfly ships a browser that has to match a real one across hundreds of signals · and math is one of the harder ones. »* On lit donc un **attaquant du problème de détection** · qui documente le canal parce qu'il doit le neutraliser.