web-dev-qa-db-fra.com

Comment arrondir un nombre à virgule flottante en Perl?

Comment puis-je arrondir un nombre décimal (virgule flottante) à l'entier le plus proche?

par exemple.

1.2 = 1
1.7 = 2
167
Ranguard

Sortie de perldoc -q round

Est-ce que Perl a une fonction round ()? Qu'en est-il de ceil () et de floor ()? Fonctions Trig?

Rappelez-vous que int() ne fait que tronquer vers _0_. Pour arrondir à un certain nombre de chiffres, sprintf() ou printf() est généralement la route la plus simple.

_    printf("%.3f", 3.1415926535);       # prints 3.142
_

Le module POSIX (composant de la distribution Perl standard) implémente ceil(), floor() et un certain nombre d'autres fonctions mathématiques et trigonométriques.

_    use POSIX;
    $ceil   = ceil(3.5);                        # 4
    $floor  = floor(3.5);                       # 3
_

Dans 5.000 à 5.003 perls, la trigonométrie a été effectuée dans le module Math::Complex . Avec 5.004, le module Math::Trig (composant de la distribution Perl standard) implémente les fonctions trigonométriques. En interne, il utilise le module Math::Complex et certaines fonctions peuvent sortir de l’axe réel dans le plan complexe, par exemple le sinus inverse de 2.

Arrondir des applications financières peut avoir de graves conséquences, et la méthode d'arrondissement utilisée doit être spécifiée avec précision. Dans ces cas, il vaut probablement mieux ne pas se fier au système d'arrondi utilisé par Perl, mais mettre en œuvre la fonction d'arrondi dont vous avez besoin.

Pour voir pourquoi, remarquez que vous aurez toujours un problème d'alternance à mi-chemin:

_    for ($i = 0; $i < 1.01; $i += 0.05) { printf "%.1f ",$i}

    0.0 0.1 0.1 0.2 0.2 0.2 0.3 0.3 0.4 0.4 0.5 0.5 0.6 0.7 0.7
    0.8 0.8 0.9 0.9 1.0 1.0
_

Ne blâmez pas Perl. C'est la même chose qu'en C. L'IEEE dit que nous devons faire cela. Les nombres Perl dont les valeurs absolues sont des entiers inférieurs à _2**31_ (sur les machines 32 bits) fonctionneront plus ou moins comme des entiers mathématiques. Les autres numéros ne sont pas garantis.

191
Vinko Vrsalovic

Bien que vous ne soyez pas en désaccord avec les réponses complexes concernant les demi-marques, etc., pour le cas d'utilisation le plus courant (et peut-être trivial):

my $rounded = int($float + 0.5);

UPDATE

S'il est possible que votre $float soit négatif, la variation suivante produira le résultat correct:

my $rounded = int($float + $float/abs($float*2 || 1));

Avec ce calcul, -1,4 est arrondi à -1, et -1,6 à -2 et zéro n'explosera pas.

127
RET

Vous pouvez soit utiliser un module comme Math :: Round :

use Math::Round;
my $rounded = round( $float );

Ou vous pouvez le faire de manière grossière:

my $rounded = sprintf "%.0f", $float;
71
EvdB

Si vous décidez d'utiliser printf ou sprintf, notez qu'ils utilisent la méthode arrondir la moitié au même .

foreach my $i ( 0.5, 1.5, 2.5, 3.5 ) {
    printf "$i -> %.0f\n", $i;
}
__END__
0.5 -> 0
1.5 -> 2
2.5 -> 2
3.5 -> 4
45
Kyle

Voir perldoc/perlfaq :

Rappelez-vous que int() ne fait que tronquer vers 0. Pour arrondir à un certain nombre de chiffres, sprintf() ou printf() est généralement le chemin le plus simple.

 printf("%.3f",3.1415926535);
 # prints 3.142

Le module POSIX (composant de la distribution Perl standard) implémente ceil(), floor() et un certain nombre d'autres fonctions mathématiques et trigonométriques.

use POSIX;
$ceil  = ceil(3.5); # 4
$floor = floor(3.5); # 3

Dans 5.000 à 5.003 perls, la trigonométrie a été effectuée dans le module Math::Complex.

Avec 5.004, le module Math::Trig (qui fait partie de la distribution Perl standard)> implémente les fonctions trigonométriques.

En interne, il utilise le module Math::Complex et certaines fonctions peuvent sortir de l’axe réel dans le plan complexe, par exemple le sinus inverse de 2.

Arrondir des applications financières peut avoir de graves conséquences, et la méthode d'arrondissement utilisée doit être spécifiée avec précision. Dans ces cas, il vaut probablement mieux ne pas se fier au système d'arrondi utilisé par Perl, mais mettre en œuvre la fonction d'arrondi dont vous avez besoin.

Pour voir pourquoi, remarquez que vous aurez toujours un problème d'alternance à mi-chemin:

for ($i = 0; $i < 1.01; $i += 0.05)
{
   printf "%.1f ",$i
}

0.0 0.1 0.1 0.2 0.2 0.2 0.3 0.3 0.4 0.4 0.5 0.5 0.6 0.7 0.7 0.8 0.8 0.9 0.9 1.0 1.0

Ne blâmez pas Perl. C'est la même chose qu'en C. L'IEEE dit que nous devons faire cela. Les nombres Perl dont les valeurs absolues sont des entiers inférieurs à 2 ** 31 (sur les machines 32 bits) fonctionneront plus ou moins comme des entiers mathématiques. Les autres numéros ne sont pas garantis.

8
Kent Fredric

Vous n'avez besoin d'aucun module externe.

$x[0] = 1.2;
$x[1] = 1.7;

foreach (@x){
  print $_.' = '.( ( ($_-int($_))<0.5) ? int($_) : int($_)+1 );
  print "\n";
}

J'ignore peut-être ce que vous voulez dire, mais j’ai pensé que c’était une façon beaucoup plus propre de faire le même travail.

Cela consiste à parcourir chaque nombre positif dans l'élément, à imprimer le nombre et l'entier arrondi dans le format que vous avez mentionné. Le code concatène le nombre entier positif arrondi respectif uniquement en fonction des nombres décimaux. int ($ _) fondamentalement arrondi le nombre so ($ - int ($)) capture les nombres décimaux. Si les décimales sont (par définition) strictement inférieures à 0,5, arrondissez le nombre. Sinon, arrondissez en ajoutant 1.

3
activealexaoki

Les nombres négatifs peuvent ajouter certaines bizarreries que les gens doivent connaître.

Les approches de style printf- nous donnent des nombres corrects, mais elles peuvent donner des affichages bizarres. Nous avons découvert que cette méthode (à mon avis, bêtement) met dans un signe - si elle devrait ou non. Par exemple, -0,01 arrondi à une décimale renvoie -0,0, plutôt que 0. Si vous allez utiliser l'approche de style printf et que vous savez que vous ne voulez pas de décimale, utilisez %d et pas %f (lorsque vous avez besoin de décimales, c'est lorsque l'affichage devient insignifiant).

Bien que ce soit correct et pour les mathématiques, ce n’est pas grave, mais pour l’affichage, cela semble bizarre, montrant quelque chose comme "-0.0".

Pour la méthode int, les nombres négatifs peuvent modifier le résultat souhaité (bien que certains arguments puissent être définis, ils sont corrects).

Le int + 0.5 pose de vrais problèmes avec les nombres négatifs, à moins que vous ne vouliez que cela fonctionne ainsi, mais j'imagine que la plupart des gens ne le font pas. -0,9 devrait probablement arrondir à -1 et non pas 0. Si vous savez que vous voulez que le négatif soit un plafond plutôt qu'un sol, vous pouvez le faire en une ligne, sinon vous pouvez utiliser la méthode int avec un mineur modification (cela ne fonctionne évidemment que pour récupérer des nombres entiers:

my $var = -9.1;
my $tmpRounded = int( abs($var) + 0.5));
my $finalRounded = $var >= 0 ? 0 + $tmpRounded : 0 - $tmpRounded;
1
matt

Voici un exemple de cinq façons différentes de additionner des valeurs. Le premier est un moyen naïf d’effectuer la sommation (et échoue). La seconde tente d’utiliser sprintf(), mais elle échoue aussi. Le troisième utilise sprintf() avec succès tandis que les deux derniers (4ème et 5ème) utilisent floor($value + 0.5).

 use strict;
 use warnings;
 use POSIX;

 my @values = (26.67,62.51,62.51,62.51,68.82,79.39,79.39);
 my $total1 = 0.00;
 my $total2 = 0;
 my $total3 = 0;
 my $total4 = 0.00;
 my $total5 = 0;
 my $value1;
 my $value2;
 my $value3;
 my $value4;
 my $value5;

 foreach $value1 (@values)
 {
      $value2 = $value1;
      $value3 = $value1;
      $value4 = $value1;
      $value5 = $value1;

      $total1 += $value1;

      $total2 += sprintf('%d', $value2 * 100);

      $value3 = sprintf('%1.2f', $value3);
      $value3 =~ s/\.//;
      $total3 += $value3;

      $total4 += $value4;

      $total5 += floor(($value5 * 100.0) + 0.5);
 }

 $total1 *= 100;
 $total4 = floor(($total4 * 100.0) + 0.5);

 print '$total1: '.sprintf('%011d', $total1)."\n";
 print '$total2: '.sprintf('%011d', $total2)."\n";
 print '$total3: '.sprintf('%011d', $total3)."\n";
 print '$total4: '.sprintf('%011d', $total4)."\n";
 print '$total5: '.sprintf('%011d', $total5)."\n";

 exit(0);

 #$total1: 00000044179
 #$total2: 00000044179
 #$total3: 00000044180
 #$total4: 00000044180
 #$total5: 00000044180

Notez que floor($value + 0.5) peut être remplacé par int($value + 0.5) pour supprimer la dépendance sur POSIX.

1
David Beckman

Les éléments suivants arrondiront les nombres positifs ou négatifs à une position décimale donnée:

sub round ()
{
    my ($x, $pow10) = @_;
    my $a = 10 ** $pow10;

    return (int($x / $a + (($x < 0) ? -0.5 : 0.5)) * $a);
}
1
seacoder

Si vous souhaitez uniquement obtenir une valeur entière sur un nombre entier à virgule flottante (c'est-à-dire 12347.9999 ou 54321.0001), cette approche (empruntée et modifiée ci-dessus) fera l'affaire:

my $rounded = floor($float + 0.1); 
0
HoldOffHunger

Ma solution pour sprintf

if ($value =~ m/\d\..*5$/){
    $format =~ /.*(\d)f$/;
    if (defined $1){
       my $coef = "0." . "0" x $1 . "05";    
            $value = $value + $coef;    
    }
}

$value = sprintf( "$format", $value );
0
Akvel