web-dev-qa-db-fra.com

Comment convertir un nombre à virgule flottante en entier?

Est-ce la bonne façon de faire une conversion float en entier dans bash? Existe-t-il une autre méthode?

flotToint() {
    printf "%.0f\n" "$@"
}
49
Rahul Patil

bash

Dans bash, c'est probablement aussi bon que possible. Cela utilise un shell intégré. Si vous avez besoin du résultat dans une variable, vous pouvez utiliser la substitution de commandes ou la spécification bash (bien que maintenant également prise en charge par zsh):

printf -v int %.0f "$float"

Vous pourriez faire:

float=1.23
int=${float%.*}

Mais cela supprimerait la partie fractionnaire au lieu de vous donner l'entier le plus proche et cela ne fonctionnerait pas pour des valeurs de $float Comme 1.2e9 Ou .12 Par exemple.

Notez également les limitations possibles dues à la représentation interne des flotteurs:

$ printf '%.0f\n' 1e50
100000000000000007629769841091887003294964970946560

Vous obtenez un entier, mais il est probable que vous ne pourrez pas utiliser cet entier n'importe où.

De plus, comme indiqué par @BinaryZebra, dans plusieurs implémentations printf (bash, ksh93, yash, pas GNU, zsh, dash), il est affecté par les paramètres régionaux (le séparateur décimal qui peut être . ou ,).

Donc, si vos flottants sont toujours exprimés avec le point comme séparateur décimal et que vous souhaitez qu'il soit traité comme tel par printf, indépendamment des paramètres régionaux de l'utilisateur qui appelle votre script, vous devez corriger les paramètres régionaux à C:

LC_ALL=C printf '%.0f' "$float"

Avec yash, vous pouvez également faire:

printf '%.0f' "$(($float))"

(voir ci-dessous).

POSIX

printf "%.0f\n" 1.1

n'est pas POSIX car %f ne doit pas être pris en charge par POSIX.

POSIX, vous pouvez faire:

f2i() {
  awk 'BEGIN{for (i=1; i<ARGC;i++)
   printf "%.0f\n", ARGV[i]}' "$@"
}

Celui-ci n'est pas affecté par les paramètres régionaux (la virgule ne peut pas être un séparateur décimal dans awk car il y a déjà un caractère spécial dans la syntaxe (print 1,2, Identique à print 1, 2 Pour passez deux arguments à print)

zsh

Dans zsh (qui prend en charge l'arithmétique à virgule flottante (le séparateur décimal est toujours le point)), vous disposez de la fonction mathématique rint() pour vous donner l'entier le plus proche sous forme de flottant (comme dans C) et int() pour vous donner un entier à partir d'un flottant (comme dans awk). Vous pouvez donc faire:

$ zmodload zsh/mathfunc
$ i=$((int(rint(1.234e2))))
$ echo $i
123

Ou:

$ integer i=$((rint(5.678e2)))
$ echo $i
568

Notez cependant que si double peut représenter de très grands nombres, les entiers sont beaucoup plus limités.

$ printf '%.0f\n' 1e123
999999999999999977709969731404129670057984297594921577392083322662491290889839886077866558841507631684757522070951350501376
$ echo $((int(1e123)))
-9223372036854775808

ksh93

ksh93 a été le premier shell de type Bourne à prendre en charge l'arithmétique à virgule flottante. ksh93 optimise la substitution de commandes en n'utilisant pas de canal ou de fourche lorsque les commandes ne sont que des commandes intégrées. Donc

i=$(printf '%.0f' "$f")

ne bifurque pas. Ou encore mieux:

i=${ printf '%.0f' "$f"; }

ce qui ne débouche pas non plus mais ne va pas non plus se donner la peine de créer un faux environnement de sous-shell.

Vous pouvez également faire:

i=$((rint(f)))

Mais attention:

$ echo "$((rint(1e18)))"
1000000000000000000
$ echo "$((rint(1e19)))"
1e+19

Vous pourriez également faire:

integer i=$((rint(f)))

Mais comme pour zsh:

$ integer i=1e18
$ echo "$i"
1000000000000000000
$ integer i=1e19
$ echo "$i"
-9223372036854775808

Attention, l'arithmétique à virgule flottante ksh93 Respecte le paramètre du séparateur décimal dans les paramètres régionaux (même si , Est autrement un opérateur mathématique ($((1,2)) serait 6/5 dans un français/Allemand ... locale, et la même chose que $((1, 2)), c'est-à-dire 2 dans une locale anglaise).

yash

yash prend également en charge l'arithmétique à virgule flottante mais n'a pas de fonctions mathématiques comme rint() de ksh93/zsh. Vous pouvez cependant convertir un nombre en entier en utilisant l'opérateur binaire o par exemple (fonctionne également dans zsh mais pas dans ksh93). Notez cependant qu'il tronque la partie décimale, il ne vous donne pas l'entier le plus proche:

$ echo "$((0.237e2 | 0))"
23
$ echo "$((1e19))"
-9223372036854775808

yash honore le séparateur décimal des paramètres régionaux en sortie, mais pas pour les constantes littérales à virgule flottante dans ses expressions arithmétiques, ce qui peut provoquer des surprises:

$ LC_ALL=fr_FR.UTF-8 ./yash -c 'a=$((1e-2)); echo $(($a + 1))'
./yash: arithmetic: `,' is not a valid number or operator

C'est une bonne chose dans la mesure où vous pouvez utiliser des constantes à virgule flottante dans vos scripts qui utilisent la période et ne pas avoir à vous soucier que cela cessera de fonctionner dans d'autres paramètres régionaux, mais tout de même être capable de gérer les nombres exprimés par l'utilisateur aussi longtemps comme vous vous en souvenez:

var=$((10.3)) # and not var=10.3
... "$((a + 0.1))" # and not "$(($a + 0.1))".

printf '%.0f\n' "$((10.3))" # and not printf '%.0f\n' 10.3
74
Stéphane Chazelas

bc - Un langage de calculatrice à précision arbitraire

int (float) devrait ressembler à:

$ echo "$float/1" | bc 
1234

Pour arrondir mieux utiliser ceci:

$ echo "($float+0.5)/1" | bc 

Exemple:

$ float=1.49
$ echo "($float+0.5)/1" | bc 
1
$ float=1.50
$ echo "($float+0.5)/1" | bc 
2
23
innocent-world

La réponse précédente soumise était presque correcte: "Vous pourriez faire:

float=1.23
int=${float%.*}

Mais cela supprimerait la partie fractionnaire au lieu de vous donner l'entier le plus proche et cela ne fonctionnerait pas pour les valeurs de $ float comme 1.2e9 ou .12 par exemple .... "

Utilisez simplement ${float%%.*}.

echo ${float%%.*}
1
6
Jay Mayers

installez le programme de bip avec:

aptitude install beep
or
apt-get install beep

et écoutez la fonction sinus comme ceci:

1a. définir la fonction en bash:

$ function sinus () 
{ 
    a=$1;
    b=$2;
    n=$3;
    f=$4;
    d=$(echo "scale=$f; ($b-$a)/$n"|bc);
    for m in $(seq 0 $n);
    do
        x=$(echo "scale=$f;$a+$m*$d"|bc);
        y=$(echo "scale=$f;s($x)"|bc -l);
        #printf "%0.${f}f\t" "$x";
        #printf "%0.${f}f\n" "$y";
        z=$(echo "scale=$f; 2300+2000*$y" | bc);
        beep -f "$z" -l 2 -d 0;
    done | column -t
}

2a. Appelez cette fonction comme ceci:

$ sinus 1 20 300 4

Note: 'sinus abnf' signifie écouter le son du sinus (en radians) de a à b divisant l'intervalle en n + 1 parties égales en utilisant f décimales. Il y a plus de choses que nécessaire dans la fonction car vous pouvez les utiliser pour tracer ou tout autre chose. Vous pouvez également écrire lire un b n f au lieu des initialisations.

Aussi, très drôle, écraser de plus en plus les cycles de la fonction sinus jusqu'à ce que le programme ne le supporte pas:

1b. définir:

$ function sinus-inv () 
{ 
    a=$1; b=$2; n=$3; f=$4;
    d=$(echo "scale=$f; ($b-$a)/$n"|bc);
    for m in $(seq 1 $n);
    do
        x=$(echo "scale=$f;$a+$m*$d"|bc);
        y=$(echo "scale=$f;s(8000/($b-$x))"|bc -l);
        z=$(echo "scale=$f; 2300+2000*$y" | bc);
        beep -f "$z" -l 1 -d 0;
    done;
}

2b. Exemple d'appel:

$ sinus-inv 1 100 1000 8
4
Robert

Un hacky très simple est

function float_to_int() { 
  echo $1 | cut -d. -f1    # or use -d, if decimals separator is ,
}

Exemple de sortie

$ float_to_int 32.333
32
$ float_to_int 32
32
4
GMaster

En relation:

  1. comment faire flotter en entier dans awk ,
  2. Comment arrondir des nombres à virgule flottante dans le shell?

Compléter @ Stéphane Chazelasawk réponse:

float_to_integer_rounding_nearst() {
    awk 'BEGIN{for (i=1; i<ARGC;i++) printf "%.0f", ARGV[i]}' "$@";
}

float_to_integer_rounding_floor() {
    awk 'BEGIN{for (i=1; i<ARGC;i++) printf "%d", int( ARGV[i] )}' "$@";
}
0
user