51 votes

Comment arrondir les décimales en utilisant bc dans bash ?

Un exemple rapide de ce que je veux en utilisant un script bash :

#!/bin/bash
echo "Insérez le prix que vous souhaitez calculer :"
read float
echo "Voici le prix sans taxes :"
echo "scale=2; $float/1.18" |bc -l
read -p "Appuyez sur une touche pour continuer..."
bash scriptname.sh

En supposant que le prix est 48.86 La réponse sera : 41.406779661 (41.40 en réalité car j'utilise scale=2;)

Ma question est : Comment arrondir le deuxième décimal pour afficher la réponse de cette manière : 41.41

3voto

Mike Rifgin Points 1059

Implémentation de bc pure comme demandée

définir ceil(x) { auto os,xx;x=-x;os=scale;scale=0 xx=x/1;if(xx>x).=xx-- scale=os;return(-xx) }

si vous mettez cela dans un fichier appelé functions.bc alors vous pouvez arrondir vers le haut avec

echo 'ceil(3.1415)' | bc functions.bc

Code pour l'implémentation bc trouvé sur http://phodd.net/gnu-bc/code/funcs.bc

2voto

user525061 Points 21

Si vous avez le résultat, par exemple considérez 2.3747888

tout ce que vous avez à faire est :

d=$(echo "(2.3747888+0.5)/1" | bc); echo $d

cela arrondit correctement le nombre exemple:

(2.49999 + 0.5)/1 = 2.99999 

les décimales sont supprimées par bc et donc cela arrondit à 2 comme il devrait

1voto

syntaxerror Points 521
#!/bin/bash
# - basé en partie sur la fonction "round()", tirée de 
# http://stempell.com/2009/08/rechnen-in-bash/

# - inspiré par l'utilisateur85321 @ askubuntu.com (auteur original)
#   et Aquarius Power

# la fonction round (approche alternative):

round2()
{
    v=$1
    vorig=$v
    # si négatif, négative la valeur ...
    (( $(bc <<<"$v < 0") == 1 )) && v=$(bc <<<"$v * -1")
    r=$(bc <<<"scale=$3;(((10^$3)*$v/$2)+0.5)/(10^$3)")

    # ... cependant, puisque la valeur a été seulement négative pour obtenir un arrondi correct, nous 
    # devons ajouter de nouveau le signe négatif pour la valeur résultante ...

    (( $(bc <<< "$vorig < 0") == 1 )) && r=$(bc <<< "$r * -1")
    env printf %.$3f $r
};

echo "Insérez le prix que vous voulez calculer :"
read float
echo "Voici le prix sans taxes :"
round2 $float 1.18 2
echo && read -p "Appuyez sur une touche pour continuer..."

En réalité, c'est simple : il n'est pas nécessaire d'ajouter explicitement une variante "-0.5" codée en dur pour les nombres négatifs. Mathématiquement parlant, nous allons simplement calculer la valeur absolue de l'argument et ajouter toujours 0.5 comme nous le ferions normalement. Mais comme nous (malheureusement) n'avons pas de fonction abs() intégrée à notre disposition (à moins d'en créer une), nous allons simplement négativer l'argument s'il est négatif.

En outre, il s'est avéré très laborieux de travailler avec le quotient en tant que paramètre (puisque, pour ma solution, je dois pouvoir accéder séparément au dividende et au diviseur). C'est pourquoi mon script a un troisième paramètre supplémentaire.

1voto

Adam Katz Points 612

Je cherche toujours une réponse pure en bc sur la façon d'arrondir juste une valeur dans une fonction, mais voici une réponse pure en bash:

#!/bin/bash

echo "Insérez le prix que vous souhaitez calculer :"
read float
echo "Voici le prix sans taxes :"

embiggen() {
  local int precision fraction=""
  if [ "$1" != "${1#*.}" ]; then  # il y a un point décimal
    fraction="${1#*.}"       # juste les chiffres après le point
  fi
  int="${1%.*}"              # le float comme entier tronqué
  precision="${#fraction}"   # le nombre de chiffres fractionnels
  echo $(( 10**10 * $int$fraction / 10**$precision ))
}

# arrondir vers le bas si négatif
if [ "$float" != "${float#-}" ]
  then round="-5000000000"
  else round="5000000000"
fi

# calculer la réponse arrondie (sans point décimal)
answer=$(( ( `embiggen $float` * 100 + $round ) / `embiggen 1.18` ))

int=${answer%??}  # la réponse comme entier tronqué

echo $int.${answer#$int}  # réassembler avec la précision correcte

read -p "Appuyez sur une touche pour continuer..."

Essentiellement, cela extrait soigneusement les décimales, multiplie tout par 100 milliards (10¹, 10**10 en bash), ajuste la précision et l'arrondi, effectue la division réelle, divise à nouveau à la magnitude appropriée, puis réinsère le point décimal.

Étape par étape :

La fonction embiggen() attribue la forme entière tronquée de son argument à $int et enregistre les chiffres après le point dans $fraction. Le nombre de chiffres fractionnels est noté dans $precision. Le calcul multiplie 10¹ par la concaténation de $int et $fraction puis ajuste cela pour correspondre à la précision (par exemple, embiggen 48.86 devient 10¹ × 4886 / 100 et retourne 488600000000 qui est 488 600 000 000).

Nous voulons une précision finale en centièmes, donc nous multiplions le premier nombre par 100, ajoutons 5 pour des raisons d'arrondi, puis divisons le deuxième nombre. Cette attribution de $answer nous laisse cent fois la réponse finale.

Maintenant nous devons ajouter le point décimal. Nous attribuons une nouvelle valeur de $int à $answer en excluant ses deux derniers chiffres, puis echo avec un point et le $answer en excluant la valeur $int qui a déjà été prise en charge. (Peu importe le bug de mise en évidence de syntaxe qui fait qu'il semble s'agir d'un commentaire)

(Bashisme : l'exponentiation n'est pas POSIX, donc c'est un bashisme. Une solution pure POSIX nécessiterait des boucles pour ajouter des zéros plutôt que d'utiliser des puissances de dix. En outre, "embiggen" est un mot tout à fait cromulent.)


Une des principales raisons pour lesquelles j'utilise zsh comme shell est qu'elle prend en charge les mathématiques en virgule flottante. La solution à cette question est assez simple en zsh :

printf %.2f $((float/1.18))

(J'adorerais voir quelqu'un ajouter un commentaire à cette réponse avec l'astuce pour activer l'arithmétique en virgule flottante dans bash, mais je suis assez sûr qu'une telle fonctionnalité n'existe pas encore.)

1voto

Marcus Kolenda Points 26

Je sais que c'est une vieille question, mais j'ai une solution pure 'bc' sans 'if' ni branches :

#!/bin/sh
bcr()
{
    echo "scale=$2+1;t=$1;scale-=1;(t*10^scale+((t>0)-(t<0))/2)/10^scale" | bc -l
}

Utilisez-le comme bcr '2/3' 5 ou bcr '0.666666' 2 --> (expression suivie de l'échelle)

C'est possible car dans bc (comme en C/C++), il est autorisé de mélanger des expressions logiques dans vos calculs. L'expression ((t>0)-(t<0))/2) évaluera à +/-0,5 en fonction du signe de 't' et utilisera donc la bonne valeur pour arrondir.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X