2 votes

zsh perd de la précision lors de la miltiplication de deux flottants de magnitude significativement différente

J'essaie de faire des maths à virgule flottante dans un zsh script. Je constate le comportement suivant :

$ (( a = 1.23456789 * 0.00000001 )); printf "a = %g\n" $a
a = 1.23e-08

$ (( a = 1.23456789 * 0.00000001 )); printf "a = %e\n" $a
a = 1.230000e-08

$ (( a = 1.23456789 * 0.0000001 )); printf "a = %e\n" $a
a = 1.235000e-07

Je m'attends à ne pas perdre la précision de la mantisse du premier nombre lorsque je le multiplie simplement par un nombre dont la mantisse est 1 (ou du moins très proche de 1, si l'on considère la véritable représentation binaire). En d'autres termes, je m'attendrais à obtenir a = 1.23456789e-08 ou peut-être une mantisse tronquée, mais pas de zéros après 1.23  / 1.235 .

J'utilise la version suivante :

$ zsh --version
zsh 5.8 (x86_64-apple-darwin20.0)

Est-ce que j'ai manqué quelque chose ? Ou est-ce un problème dans zsh ? Je suis nouveau dans zsh, et je n'ai pas beaucoup d'expérience dans la programmation shell en général, donc toute aide est appréciée. Merci !

2voto

that other guy Points 26297

Il semble que (( x = 1.0 )) lorsque x n'est pas défini, Zsh déclarera la variable en tant que -F : un point flottant de double précision qui est formaté en point fixe avec 10 chiffres décimaux en sortie :

% unset x; (( x = 0.12345678901234567 )); declare -p x
typeset -F x=0.1234567890

% unset x; x=$((0.12345678901234567)); declare -p x
typeset x=0.12345678901234566

Je ne sais pas pourquoi cela fonctionne ainsi, mais si vous déclarez d'abord manuellement votre variable comme une chaîne de caractères, cela ne se produira pas et vous obtiendrez la valeur complète :

% unset a; typeset a; (( a = 1.23456789 * 0.00000001 )); printf "a = %g\n" $a
a = 1.23457e-08

1voto

user1934428 Points 3811

La différence vient de la manière dont vous passez la valeur de l'option a a printf . Si vous l'écrivez comme

(( a = 1.23456789 * 0.00000001 )); printf "a = %e\n" $((a))

$ (( a = 1.23456789 * 0.0000001 )) ; printf "a = %e \n " $((a))

le problème ne se produit pas. Ceci est décrit aquí où il est dit :

Les nombres à virgule flottante peuvent être déclarés avec le constructeur float ; il en existe deux types, qui ne diffèrent que par leur format de sortie, comme décrit pour le constructeur typeset. Le format de sortie peut être contourné en utilisant la substitution arithmétique au lieu de la substitution de paramètre, c'est-à-dire que '${float}' utilise le format défini, mais '$((float))' utilise un format générique à virgule flottante.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X