Comment arrondir les nombres décimaux à l'aide de bc dans bash?

35

Un exemple rapide de ce que je veux utiliser les scripts bash:

#!/bin/bash
echo "Insert the price you want to calculate:"
read float
echo "This is the price without taxes:"
echo "scale=2; $float/1.18" |bc -l
read -p "Press any key to continue..."
bash scriptname.sh

En supposant que le prix soit: 48,86 La réponse sera: 41.4067796661 (41.40 parce que j'utilise scale=2; )

Ma question est la suivante: Comment puis-je arrondir la deuxième décimale pour afficher la réponse de cette manière ?: 41.41

    
posée blackedx 24.08.2012 - 16:25
la source

12 réponses

28

Une fonction bash round:

round()
{
echo $(printf %.$2f $(echo "scale=$2;(((10^$2)*$1)+0.5)/(10^$2)" | bc))
};

Utilisé dans votre exemple de code:

#!/bin/bash
# the function "round()" was taken from 
# http://stempell.com/2009/08/rechnen-in-bash/

# the round function:
round()
{
echo $(printf %.$2f $(echo "scale=$2;(((10^$2)*$1)+0.5)/(10^$2)" | bc))
};

echo "Insert the price you want to calculate:"
read float
echo "This is the price without taxes:"
#echo "scale=2; $float/1.18" |bc -l
echo $(round $float/1.18 2);
read -p "Press any key to continue..."

Bonne chance: o)

    
réponse donnée user85321 24.08.2012 - 18:24
la source
24

La solution la plus simple:

printf %.2f $(echo "$float/1.18" | bc -l)
    
réponse donnée migas 16.01.2015 - 17:39
la source
17

Arrondir avec / awk:

echo "23.49" | awk '{printf("%d\n",$1 + 0.5)}'  

Si vous avez du python, vous pouvez utiliser quelque chose comme ceci:

echo "4.678923" | python -c "print round(float(raw_input()))"
    
réponse donnée zuberuber 24.08.2012 - 16:28
la source
3

Voici une solution purement bc. Règles d'arrondi: à +/- 0,5, arrondir à zéro.

Mettez la balance que vous cherchez dans $ result_scale; votre calcul devrait être où $ MATH est situé dans la liste de commandes bc:

bc <<MATH
h=0
scale=0

/* the magnitude of the result scale */
t=(10 ^ $result_scale)

/* work with an extra digit */
scale=$result_scale + 1

/* your math into var: m */
m=($MATH)

/* rounding and output */
if (m < 0) h=-0.5
if (m > 0) h=0.5

a=(m * t + h)

scale=$result_scale
a / t
MATH
    
réponse donnée MetroEast 26.08.2015 - 21:11
la source
2

Je sais que c'est une vieille question, mais j'ai une solution pure 'bc' sans 'if' ou 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 d'une échelle)

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

    
réponse donnée Marcus Kolenda 29.04.2017 - 01:59
la source
1
#!/bin/bash
# - loosely based on the function "round()", taken from 
# http://stempell.com/2009/08/rechnen-in-bash/

# - inspired by user85321 @ askubuntu.com (original author)
#   and Aquarius Power

# the round function (alternate approach):

round2()
{
    v=$1
    vorig=$v
    # if negative, negate value ...
    (( $(bc <<<"$v < 0") == 1 )) && v=$(bc <<<"$v * -1")
    r=$(bc <<<"scale=$3;(((10^$3)*$v/$2)+0.5)/(10^$3)")

    # ... however, since value was only negated to get correct rounding, we 
    # have to add the minus sign again for the resulting value ...

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

echo "Insert the price you want to calculate:"
read float
echo "This is the price without taxes:"
round2 $float 1.18 2
echo && read -p "Press any key to continue..."

C’est en fait 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 toujours add 0.5 comme nous le ferions normalement. Mais comme nous ne disposons (malheureusement) pas de fonction abs() intégrée (sauf si nous en codons une), nous allons simplement nier l'argument s'il est négatif.

De plus, travailler avec le quotient en tant que paramètre s'est avéré très fastidieux (car pour ma solution, je dois être en mesure d'accéder au dividende et au diviseur séparément). C'est pourquoi mon script a un troisième paramètre supplémentaire.

    
réponse donnée syntaxerror 24.10.2014 - 01:56
la source
1

Je cherche toujours une pure réponse bc expliquant comment arrondir une seule valeur dans une fonction, mais voici une pure réponse bash :

#!/bin/bash

echo "Insert the price you want to calculate:"
read float
echo "This is the price without taxes:"

embiggen() {
  local int precision fraction=""
  if [ "$1" != "${1#*.}" ]; then  # there is a decimal point
    fraction="${1#*.}"       # just the digits after the dot
  fi
  int="${1%.*}"              # the float as a truncated integer
  precision="${#fraction}"   # the number of fractional digits
  echo $(( 10**10 * $int$fraction / 10**$precision ))
}

# round down if negative
if [ "$float" != "${float#-}" ]
  then round="-5000000000"
  else round="5000000000"
fi

# calculate rounded answer (sans decimal point)
answer=$(( ( 'embiggen $float' * 100 + $round ) / 'embiggen 1.18' ))

int=${answer%??}  # the answer as a truncated integer

echo $int.${answer#$int}  # reassemble with correct precision

read -p "Press any key to continue..."

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

Pas à pas:

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

Nous voulons une précision finale de centièmes. Nous multiplions donc le premier nombre par 100, en ajoutions 5 à des fins d’arrondi, puis nous divisons le deuxième nombre. Cette assignation de $answer nous laisse cent fois la réponse finale.

Nous devons maintenant ajouter le point décimal. Nous assignons une nouvelle valeur $int à $answer en excluant ses deux derniers chiffres, puis echo avec un point et en $answer en excluant la valeur $int déjà prise en charge. (Peu importe le bogue de mise en évidence de la syntaxe qui le fait apparaître comme un commentaire)

(Bashism: l’exponentiation n’est pas POSIX, c’est donc un penchant. Une solution POSIX pure nécessiterait des boucles pour ajouter des zéros plutôt que d’utiliser des puissances de dix. En outre, "embiggen" est un mot parfaitement codant.)

L'une des principales raisons pour lesquelles j'utilise zsh en tant que shell est qu'il prend en charge les mathématiques en virgule flottante. La solution à cette question est assez simple dans zsh :

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

(J'aimerais beaucoup que quelqu'un ajoute un commentaire à cette réponse en essayant d'activer l'arithmétique en virgule flottante dans bash , mais je suis à peu près sûr qu'une telle fonctionnalité n'existe pas encore.)

    
réponse donnée Adam Katz 22.05.2015 - 02:35
la source
1

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

tout ce que vous avez à faire est:

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

ceci arrondit le nombre correctement exemple:

(2.49999 + 0.5)/1 = 2.99999 

les décimales sont supprimées de bc , de sorte qu'il est arrondi à 2 comme il se doit

    
réponse donnée user525061 31.03.2016 - 12:24
la source
1

Je devais calculer la durée totale d'une collection de fichiers audio.

Je devais donc:

A. obtenir la durée de chaque fichier ( non affiché )

B. additionnez toutes les durées (elles étaient chacune en NNN.NNNNNN (fp) secondes

C. séparer heures, minutes, secondes, sous-secondes.

D. produire une chaîne de caractères HR: MIN: SEC: FRAMES, où frame = 1/75 sec.

(Les images proviennent du code SMPTE utilisé dans les studios.)

A: utilisez ffprobe et analysez la ligne de durée dans un numéro fp (non affiché)

B:

 # add them up as a series of strings separated by "+" and send it to bc

arr=( "${total[@]}" )  # copy array

# IFS is "Internal Field Separator"
# the * in arr[*] means "all of arr separated by IFS" 
# must have been made for this
IFS='+' sum=$(echo "scale=3; ${arr[*]} "| bc -l)# (-l= libmath for fp)
echo $sum 

C:

# subtract each amount of time from tt and store it    
tt=$sum   # tt is a running var (fp)


hrs=$(echo "$tt / 3600" | bc)

tt=$(echo "$tt - ( $hrs * 3600 )" | bc )

min=$(echo "$tt / 60" | bc )

tt=$(echo "$tt - ($min *60)" | bc )

sec=$(echo "$tt/1" | bc )

tt=$(echo "$tt - $sec" | bc )

frames=$(echo "$tt * 75"  | bc ) # 75 frames /sec 
frames=$(echo "$frames/1" | bc ) # truncate to whole #

D:

#convert to proper format with printf (bash builtin)        
hrs=$(printf "%02d\n" $hrs)  # format 1 -> 01 

min=$(printf "%02d\n" $min)

sec=$(printf "%02d\n" $sec)

frames=$(printf "%02d\n" $frames)

timecode="$hrs:$min:$sec:$frames"

# timecode "01:13:34:54"
    
réponse donnée Chris Reid 08.06.2017 - 02:31
la source
1

Voici une version abrégée de votre script, corrigée pour fournir le résultat souhaité:

#!/bin/bash
float=48.86
echo "You asked for $float; This is the price without taxes:"
echo "scale=3; price=$float/1.18 +.005; scale=2; price/1 " | bc

Notez que l’arrondi au nombre entier le plus proche équivaut à ajouter 0,5 et à prendre la parole ou à arrondir à la baisse (pour les nombres positifs).

De plus, le facteur d'échelle est appliqué au moment de l'opération; alors (Ce sont des commandes bc , vous pouvez les coller dans votre terminal):

float=48.86; rate=1.18; 
scale=2; p2=float/rate
scale=3; p3=float/rate
scale=4; p4=float/rate
print "Compare:  ",p2, " v ", p3, " v ", p4
Compare:  41.40 v 41.406 v 41.4067

# however, scale does not affect an entered value (nor addition)
scale=0
a=.005
9/10
0
9/10+a
.005

# let's try rounding
scale=2
p2+a
41.405
p3+a
41.411
(p2+a)/1
41.40
(p3+a)/1
41.41
    
réponse donnée toddkaufmann 27.11.2017 - 09:35
la source
1

Implémentation pure-bc à la demande

define 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 avec

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

Code de l'implémentation bc trouvé sur le lien

.     
réponse donnée Aethalides 16.04.2018 - 21:14
la source
0
bc() {
        while :
        do
                while IFS='$\n' read i
                do
                        /usr/bin/bc <<< "scale=2; $i" | sed 's/^\./0&/'
                done
        done
}
    
réponse donnée 13.10.2018 - 14:40
la source

Lire d'autres questions sur les étiquettes