November 2008 Archive

Devel::NYTProf ist ein recht neuer, aber sehr leistungsfähiger Profiler für Perl, der von Adam Kaplan für die New York Times (daher der Name) entwickelt wurde. Tim Bunce, bekannt als Enwickler der Datenbankschnittstelle DBI, entwickelt Devel::NYTProf weiter.

Zeilen und Unterprogramme

Zeilenorientierte Profiler wie Devel::SmallProf bzw. Devel::FastProf von Salvador Fandiño García zeigen, welche Zeile eines Perl-Programms wie oft aufgerufen wurde und wieviel Zeit dafür benötigt wurde.

Unterprogrammorientierte Profiler wie Devel::DProf von Ilya Zakharevich zeigen, welches Unterprogramm eines Perl-Programms wie oft aufgerufen wurde und wieviel Zeit dafür benötigt wurde.

Devel::NYTProf kann sowohl zeilen- als auch unterprogrammorientiert messen und läuft auf Linux/Unix und Windows.

Schauen wir uns den neuen Profiler mal etwas näher an.

Beispielprogramm: Fakulät berechnen

Das Beispielprogramm berechnet die Fakulät einer Ganzzahl auf drei unterschiedliche Arten: rekursiv und zweimal iterativ. Die Variationen habe ich dem Journal of ambs: How would you write factorial? entnommen.

#!/usr/bin/perl 
use strict;
use warnings;

# Berechnung der Fakultaet n! 

my $n = 70;

print recursive($n)  , "\n";
print iterativ($n)   , "\n";
print iterativ2($n)  , "\n";


sub recursive {
  my $n = shift;
  if ($n == 1 or $n == 0) {
     return 1;
  }
  my $total = $n * recursive($n-1);
  return $total;
}
sub iterativ {
    my $v = shift;
    my $res = 1;
    while ($v > 1) {
        $res *= $v;
        $v--;
    }
    return $res;
}
sub iterativ2 {
    my $v = shift;
    my $res = 1;
    grep {$res*=$_} (1..$v);
    return $res;
}
# Quelle:
# Journal of ambs: How would you write factorial?
# http://use.perl.org/~ambs/journal/37511

Das Programm erzeugt folgende Ausgabe:

$ perl factorial.pl
1.19785716699699e+100
1.19785716699699e+100
1.19785716699699e+100

Das ist übrigens das selbe Beispielprogramm wie in den Beiträgen In welcher Zeile verbrät mein Programm die meiste Zeit? Devel::SmallProf und Devel::FastProf geben Antworten bzw. In welchem Unterprogramm verbrät mein Programm die meiste Zeit? Devel::DProf liefert Hinweise.

Devel::NYTProf

Das Beispielprogramm lässt sich mit Devel::NYTProf wie folgt untersuchen.

$ perl -d:NYTProf factorial.pl

Devel::NYTProf erzeugt im aktuellen Verzeichnis die Datei nytprof.out, die sich mit den mitgelieferten Werkzeugen nytprofhtml bzw. nytprofcsv anzeigen lässt.

nytprofcsv

nytprofcsv bereitet das Profiling in betriebssystem-unabhängigen CSV-Dateien, die bequem mit Excel oder Open-Office.org geöffnet werden können, auf. Für jede Zeile Code werden die verbrauchte Zeit, die Anzahl Aufrufe und die Zeit/Aufruf angezeigt.

$ nytprofcsv 
Generating CSV report...
Reading nytprof.out
Writing report to nytprof directory

nytprofcsv erzeugt einen Unterordner nytprof, in dem die Profiling-Ergebnisse abgelegt werden.

$ ls -1 nytprof
factorial.pl-block.csv
factorial.pl-line.csv
factorial.pl-sub.csv
strict.pm-block.csv
strict.pm-line.csv
strict.pm-sub.csv
warnings.pm-block.csv
warnings.pm-line.csv
warnings.pm-sub.csv

Das muss man sich einfach mal in einer Tabellenkalkulation ansehen, dann weiß man sofort, wie es funktioniert.

nytprofhtml

nytprofhtml erzeugt ebenfalls einen Unterordner nytprof, in dem die Profiling-Ergebnisse als HTML-Dateien abgelegt werden.

$ nytprofhtml nytprof.out 
Generating report...
Reading nytprof.out
Writing report to nytprof directory
$ ls -1 nytprof
factorial.pl-block.html
factorial.pl-line.html
factorial.pl-sub.html
index.html
index-subs-excl.html
index-subs-incl.html
jquery.min.js
jquery.tablesorter.min.js
strict.pm-block.html
strict.pm-line.html
strict.pm-sub.html
style.css
warnings.pm-block.html
warnings.pm-line.html
warnings.pm-sub.html

Die HTML-Ausgabe sieht zunächst einmal rattenscharf aus:

NYTPP01_klein.png

und enthält alle Details, die benötigt werden, um Zeitdiebe auf zu finden.

Mein Fazit

Devel::NYTProf ist der derzeit beste Profiler für Perl und gehört in den Werkzeugkasten eines jeden Entwicklers.

Danke New York Times

Ein ganz besonderen Dank an die New York Times, die ihren Angestellten erlaubt, ein derart geiles Tool als Open-Source zu veröffentlichen.

Siehe auch:

Unterprogrammorientierte Profiler wie Devel::DProf von Ilya Zakharevich zeigen, welches Unterprogramm eines Perl-Programms wie oft aufgerufen wurde und wieviel Zeit dafür benötigt wurde. Sie erlauben es dem Entwickler, solche Bereiche gezielt zu verbessern.

Beispielprogramm: Fakulät berechnen

Das Beispielprogramm berechnet die Fakulät einer Ganzzahl auf drei unterschiedliche Arten: rekursiv und zweimal iterativ. Die Variationen habe ich dem Journal of ambs: How would you write factorial? entnommen.

#!/usr/bin/perl 
use strict;
use warnings;

# Berechnung der Fakultaet n! 

my $n = 70;

print recursive($n)  , "\n";
print iterativ($n)   , "\n";
print iterativ2($n)  , "\n";


sub recursive {
  my $n = shift;
  if ($n == 1 or $n == 0) {
     return 1;
  }
  my $total = $n * recursive($n-1);
  return $total;
}
sub iterativ {
    my $v = shift;
    my $res = 1;
    while ($v > 1) {
        $res *= $v;
        $v--;
    }
    return $res;
}
sub iterativ2 {
    my $v = shift;
    my $res = 1;
    grep {$res*=$_} (1..$v);
    return $res;
}
# Quelle:
# Journal of ambs: How would you write factorial?
# http://use.perl.org/~ambs/journal/37511

Das Programm erzeugt folgende Ausgabe:

$ perl factorial.pl
1.19785716699699e+100
1.19785716699699e+100
1.19785716699699e+100

Devel::DProf

Das Programm kann man nun mit Devel::DProf wie folgt untersuchen:

 perl -d:DProf factorial.pl 

Devel::DProf erzeugt im aktuellen Verzeichnis eine Datei tmon.out, welche die Ergebnisse enthält.

Zur Ausgabe der Ergebnisse wird das mitgelieferte Programm dprofpp benötigt.

$ dprofpp tmon.out 
Total Elapsed Time = 0.009769 Seconds
 User+System Time =        0 Seconds
Exclusive Times
%Time ExclSec CumulS #Calls sec/call Csec/c  Name
 0.00       - -0.000      1        -      -  strict::import
 0.00       - -0.000      1        -      -  warnings::import
 0.00       - -0.000      1        -      -  strict::bits
 0.00       - -0.000      1        -      -  main::iterativ
 0.00       - -0.000      1        -      -  main::iterativ2
 0.00       - -0.000      2        -      -  main::BEGIN
 0.00       - -0.000     70        -      -  main::recursive

Der Schalter -l (kleines L) sortiert die Ausgabe nach der Anzahl der Aufrufe der Unterprogramme.

$ dprofpp -l tmon.out 
Total Elapsed Time = 0.009769 Seconds
  User+System Time =        0 Seconds
Exclusive Times
%Time ExclSec CumulS #Calls sec/call Csec/c  Name
 0.00       - -0.000     70        -      -  main::recursive
 0.00       - -0.000      2        -      -  main::BEGIN
 0.00       - -0.000      1        -      -  strict::bits
 0.00       - -0.000      1        -      -  strict::import
 0.00       - -0.000      1        -      -  warnings::import
 0.00       - -0.000      1        -      -  main::iterativ
 0.00       - -0.000      1        -      -  main::iterativ2

Devel::DProf verwendet leider nur drei Nachkommastellen bei der Zeitmessung.

Abhilfe schafft hier z.B. eine Erhöhung der Anzahl der Aufrufe der Unterprogramme:

my $n = 70;
for ( 1 .. 100_000 ) {
	print recursive($n)  , "\n";
	print iterativ($n)   , "\n";
	print iterativ2($n)  , "\n";
}

Die Ausgabe von dprofpp ist nun zwar nicht völlig korrekt, aber deutlich informativer,

dprofpp -l tmon.out 
Total Elapsed Time = 19.58602 Seconds
  User+System Time =        0 Seconds
Exclusive Times
%Time ExclSec CumulS #Calls sec/call Csec/c  Name
 0.00       - -8.614 700000        -      -  main::recursive
 0.00   1.830  1.830 100000   0.0000 0.0000  main::iterativ
 0.00   2.760  2.760 100000   0.0000 0.0000  main::iterativ2
 0.00   0.010  0.010      2   0.0050 0.0050  main::BEGIN
 0.00       - -0.000      1        -      -  strict::bits
 0.00       - -0.000      1        -      -  strict::import
 0.00       - -0.000      1        -      -  warnings::import

Zeilenorientierung vs. Unterprogrammorientierung

Neben dem beschriebenem unterprogrammorientiertem Profiler Devel::DProf, gibt es noch weitere Profiler, die auf der der Basis von Zeilen arbeiten, z.B. Devel::SmallProf oder Devel::FastProf.

Devel::SmallProf und Devel::FastProf sind in dem Beitrag In welcher Zeile verbrät mein Programm die meiste Zeit? Devel::SmallProf und Devel::FastProf geben Antworten beschrieben.

Fazit

Auch wenn man Devel::DProf anmerkt, daß es bereits 1999 geschrieben wurde, ist es denoch ein einfach zu benutzendes Werkzeug, das oft hilft, Flaschenhälse aufzuspüren.

Siehe auch

Zeilenorientierte Profiler wie Devel::SmallProf bzw. Devel::FastProf von Salvador Fandiño García zeigen, welche Zeile eines Perl-Programms wie oft aufgerufen wurde und wieviel Zeit dafür benötigt wurde. Sie erlauben es dem Entwickler, solche Bereiche gezielt zu verbessern.

Beispielprogramm: Fakulät berechnen

Das Beispielprogramm berechnet die Fakulät einer Ganzzahl auf drei unterschiedliche Arten: rekursiv und zweimal iterativ. Die Variationen habe ich dem Journal of ambs: How would you write factorial? entnommen.

#!/usr/bin/perl 
use strict;
use warnings;

# Berechnung der Fakultaet n! 

my $n = 70;

print recursive($n)  , "\n";
print iterativ($n)   , "\n";
print iterativ2($n)  , "\n";


sub recursive {
  my $n = shift;
  if ($n == 1 or $n == 0) {
     return 1;
  }
  my $total = $n * recursive($n-1);
  return $total;
}
sub iterativ {
    my $v = shift;
    my $res = 1;
    while ($v > 1) {
        $res *= $v;
        $v--;
    }
    return $res;
}
sub iterativ2 {
    my $v = shift;
    my $res = 1;
    grep {$res*=$_} (1..$v);
    return $res;
}
# Quelle:
# Journal of ambs: How would you write factorial?
# http://use.perl.org/~ambs/journal/37511

Das Programm erzeugt folgende Ausgabe:

$ perl factorial.pl
1.19785716699699e+100
1.19785716699699e+100
1.19785716699699e+100

Devel::SmallProf

Das Programm lässt sich mit Devel::SmallProf wie folgt untersuchen:

perl -d:SmallProf factorial.pl

Devel::SmallProf erzeugt im aktuellen Verzeichnis eine Datei smallprof.out, welche die Ergebnisse enthält. In der ersten Spalte wird angezeigt, wie oft eine Zeile im Programm aufgerufen wurde, die zweite Spalte zeigt die tatsächlich verbrauchte Zeit, die dritte Spalte die benötigte CPU-Zeit an. Die letzte Spalte enthält den Inhalt der Zeile.

$ cat smallprof.out 
           ================ SmallProf version 2.02 ================
                            Profile of factorial.pl                    Page 1
       =================================================================
    count wall tm  cpu time line
        0   0.00000   0.00000     1:#!/usr/bin/perl
        0   0.00000   0.00000     2:use strict;
        0   0.00000   0.00000     3:use warnings;
        0   0.00000   0.00000     4:
        0   0.00000   0.00000     5:# Berechnung der Fakultaet n!
        0   0.00000   0.00000     6:
        1   0.00000   0.00000     7:my $n = 70;
        0   0.00000   0.00000     8:
        1   0.00000   0.00000     9:print recursive($n)  , "\n";
        1   0.00000   0.00000    10:print iterativ($n)   , "\n";
        1   0.00000   0.00000    11:print iterativ2($n)  , "\n";
        0   0.00000   0.00000    12:
        0   0.00000   0.00000    13:
        0   0.00000   0.00000    14:sub recursive {
       70   0.00000   0.00000    15:  my $n = shift;
       70   0.00000   0.00000    16:  if ($n == 1 or $n == 0) {
        0   0.00000   0.00000    17:     return 1;
        0   0.00000   0.00000    18:  }
       69   0.00004   0.00000    19:  my $total = $n * recursive($n-1);
       69   0.00014   0.00000    20:  return $total;
        0   0.00000   0.00000    21:}
        0   0.00000   0.00000    22:sub iterativ {
        1   0.00000   0.00000    23:    my $v = shift;
        1   0.00000   0.00000    24:    my $res = 1;
        1   0.00000   0.00000    25:    while ($v > 1) {
       69   0.00000   0.00000    26:        $res *= $v;
       69   0.00001   0.01000    27:        $v--;
        0   0.00000   0.00000    28:    }
        1   0.00009   0.00000    29:    return $res;
        0   0.00000   0.00000    30:}
        0   0.00000   0.00000    31:sub iterativ2 {
        1   0.00000   0.00000    32: my $v = shift;
        1   0.00000   0.00000    33:    my $res = 1;
        1   0.00006   0.00000    34:    grep {$res*=$_} (1..$v);
        1   0.00010   0.00000    35:    return $res;
        0   0.00000   0.00000    36:}
        0   0.00000   0.00000    37:# Quelle:
        0   0.00000   0.00000    38:# Journal of ambs: How would you write
        0   0.00000   0.00000    39:# http://use.perl.org/~ambs/journal/37511

Ausgabe nach der Anzahl der Aufrufe sortieren

Die Ausgabe lässt sich in einer Linux-Shell durch das Kommando

$    sort -k 1nr,1 smallprof.out |  head

einfach nach der ersten Spalte, der Anzahl der Aufrufe, numerisch sortieren. So stehen die am häufigsten aufgerufenen Zeilen gleich am Anfang der Ausgabe.

       70   0.00000   0.00000    15:  my $n = shift;
       70   0.00000   0.00000    16:  if ($n == 1 or $n == 0) {
       69   0.00000   0.00000    26:        $res *= $v;
       69   0.00001   0.01000    27:        $v--;
       69   0.00004   0.00000    19:  my $total = $n * recursive($n-1);
       69   0.00014   0.00000    20:  return $total;
        1   0.00000   0.00000    10:print iterativ($n)   , "\n";
        1   0.00000   0.00000    11:print iterativ2($n)  , "\n";
        1   0.00000   0.00000    23:    my $v = shift;
        1   0.00000   0.00000    24:    my $res = 1;

Ausgabe nach der tatsächlich benötigten Zeit sortieren

Die Ausgabe lässt sich in einer Linux-Shell durch das Kommando

$    sort -k 2nr,2 smallprof.out |  head

einfach nach dem zweiten Feld von links (der tatsächlich verbrauchten Zeit) numerisch sortieren. So stehen die Zeilen mit den größten Zeitfresser gleich am Anfang der Ausgabe.

       69   0.00014   0.00000    20:  return $total;
        1   0.00010   0.00000    35:    return $res;
        1   0.00009   0.00000    29:    return $res;
        1   0.00006   0.00000    34:    grep {$res*=$_} (1..$v);
       69   0.00004   0.00000    19:  my $total = $n * recursive($n-1);
       69   0.00001   0.01000    27:        $v--;

Devel::FastProf

Devel::FastProf ist eine Art Nachfolger von Devel::SmallProf.

Unter Windows läuft Devel::FastProf leider nicht

Das Beispielprogramm lässt sich mit Devel::FastProf wie folgt untersuchen:

perl -d:FastProf factorial.pl 

Devel::FastProf erzeugt im aktuellen Verzeichnis eine Datei fastprof.out, welche die Ergebnisse in einem Binärformat enthält.

Zur Ausgabe der Ergebnisse wird das mitgelieferte Programm fprofpp benötigt.

f$ fprofpp fastprof.out 

# fprofpp output format is:
# filename:line time count: source
factorial.pl:20 0.01348 69: return $total;
factorial.pl:19 0.00030 69: my $total = $n * recursive($n-1);
factorial.pl:29 0.00016 1: return $res;
factorial.pl:15 0.00015 70: my $n = shift;
factorial.pl:16 0.00015 70: if ($n == 1 or $n == 0) {
factorial.pl:26 0.00015 69: $res *= $v;
factorial.pl:27 0.00014 69: $v--;
factorial.pl:35 0.00011 1: return $res;
factorial.pl:34 0.00007 1: grep {$res*=$_} (1..$v);
factorial.pl:10 0.00001 1: print iterativ($n)   , "\n";
factorial.pl:23 0.00001 1: my $v = shift;
factorial.pl:11 0.00001 1: print iterativ2($n)  , "\n";
factorial.pl:25 0.00000 1: while ($v > 1) {
factorial.pl:32 0.00000 1: my $v = shift;
factorial.pl:9 0.00000 1: print recursive($n)  , "\n";
factorial.pl:7 0.00000 1: my $n = 70;
factorial.pl:24 0.00000 1: my $res = 1;
factorial.pl:33 0.00000 1: my $res = 1;

fprofpp erzeugt eine übersichtliche Tabelle, in der die Zeilen, die am häufigsten aufgerufen werden und die verbrauchte Zeit angezeigt werden.

Zeilenorientierung vs. Unterprogrammorientierung

Neben den beschriebenen zeilenorientierte Profilern, gibt es noch weitere Profiler, die auf der Basis von Unterprogrammen arbeiten, z.B. Devel::DProf, auf den ich in dem Beitrag In welchem Unterprogramm verbrät mein Programm die meiste Zeit? Devel::DProf liefert Hinweise eingehe.

Zeilenorientierte Profiler haben gegenüber unterprogrammorientierten Profilern den Vorteil, daß sie auch Zeitdiebe innerhalb von Subroutinen finden, z.B. langsame Regexe innerhalb eines langen Unterprogramms.

Vorsicht AUTOLOAD

Wegen der Zeilenorientierung liefern beide Profiler falsche Ergebnisse, sobald AUTOLOAD verwendet wird.

Siehe auch

Bei Perl-Schulungen werde ich häufig nach guten Links zu Tutorien oder Artikeln zu EPIC, einem Perl Plugin für die Allzweck-IDE Eclipse gefragt.

Hier die Liste, die ich üblicherweise an die Teilnehmer ausgebe:

Weitere gute Links zu EPIC bitte als Kommentar hinzufügen. Danke.

Zahlen runden kann eine recht tückische Aufgabe sein.

Das CPAN-Modul Math::Round von Geoffrey Rommel vereinfacht diese Aufgabe erheblich.

Dieser Beitrag geht auf die Themen

  • Auf Ganze Zahlen runden
  • Auf Fließkommazahlen runden, z.B. Nachkommastellen
  • Zum nächsten Vielfachen auf- oder abrunden, z.B. Hunderter
ein.

Auf Ganze Zahlen runden

Die Prozeduren round, round_even, round_odd und round_rand runden auf Ganzzahlen (Integer) auf bzw. ab.

  • round LIST rundet zur nächsten Ganzzahl auf bzw. ab.
  • round_even LIST rundet zur nächsten Ganzzahl auf bzw. ab. Zahlen, die in der Mitte zwischen zwei Ganzzahlen liegen, werden zur nächsten geraden Zahl gerundet.
  • round_odd LIST rundet zur nächsten Ganzzahl auf bzw. ab. Zahlen, die in der Mitte zwischen zwei Ganzzahlen liegen, werden zur nächsten ungeraden Zahl gerundet.
  • round_rand LIST rundet zur nächsten Ganzzahl auf bzw. ab. Zahlen, die in der Mitte zwischen zwei Ganzzahlen liegen, werden zufällig auf- bzw. abgerundet

Beispiel: Zur nächsten Ganzzahl auf- oder abrunden:

#!/usr/bin/perl 
use strict;
use warnings;
use Math::Round qw/round/;

my @zahlen = qw /-2.5 -2.0 -1.5 -1.0 -0.5 
                 0.0 
                 0.5 1.0 1.5 2.0 2.5
                 125.3456 125.5678/;

foreach my $zahl ( @zahlen ) {
	printf("%8s wird zu %8s gerundet\n", $zahl,  round($zahl));
}

Das Programm erzeugt folgende Ausgabe:

    -2.5 wird zu       -3 gerundet
    -2.0 wird zu       -2 gerundet
    -1.5 wird zu       -2 gerundet
    -1.0 wird zu       -1 gerundet
    -0.5 wird zu       -1 gerundet
     0.0 wird zu        0 gerundet
     0.5 wird zu        1 gerundet
     1.0 wird zu        1 gerundet
     1.5 wird zu        2 gerundet
     2.0 wird zu        2 gerundet
     2.5 wird zu        3 gerundet
125.3456 wird zu      125 gerundet
125.5678 wird zu      126 gerundet

Beispiel: round_even

#!/usr/bin/perl 
use strict;
use warnings;
use Math::Round qw/round_even/;


my @zahlen = qw /-2.5 -2.0 -1.5 -1.0 -0.5 
                 0.0 
                 0.5 1.0 1.5 2.0 2.5
                 125.3456 125.5678/;

foreach my $zahl ( @zahlen ) {
	printf("%8s wird zu %8s gerundet\n", $zahl,  round_even($zahl));
}

Das Programm erzeugt folgende Ausgabe:

    -2.5 wird zu       -2 gerundet
    -2.0 wird zu       -2 gerundet
    -1.5 wird zu       -2 gerundet
    -1.0 wird zu       -1 gerundet
    -0.5 wird zu        0 gerundet
     0.0 wird zu        0 gerundet
     0.5 wird zu        0 gerundet
     1.0 wird zu        1 gerundet
     1.5 wird zu        2 gerundet
     2.0 wird zu        2 gerundet
     2.5 wird zu        2 gerundet
125.3456 wird zu      125 gerundet
125.5678 wird zu      126 gerundet

Beispiel: round_odd

#!/usr/bin/perl 
use strict;
use warnings;
use Math::Round qw/round_odd/;


my @zahlen = qw /-2.5 -2.0 -1.5 -1.0 -0.5 
                 0.0 
                 0.5 1.0 1.5 2.0 2.5
                 125.3456 125.5678/;

foreach my $zahl ( @zahlen ) {
	printf("%8s wird zu %8s gerundet\n", $zahl,  round_odd($zahl));
}

Das Programm erzeugt folgende Ausgabe:

    -2.5 wird zu       -3 gerundet
    -2.0 wird zu       -2 gerundet
    -1.5 wird zu       -1 gerundet
    -1.0 wird zu       -1 gerundet
    -0.5 wird zu       -1 gerundet
     0.0 wird zu        0 gerundet
     0.5 wird zu        1 gerundet
     1.0 wird zu        1 gerundet
     1.5 wird zu        1 gerundet
     2.0 wird zu        2 gerundet
     2.5 wird zu        3 gerundet
125.3456 wird zu      125 gerundet
125.5678 wird zu      126 gerundet

Beispiel: round_rand

#!/usr/bin/perl 
use strict;
use warnings;

use Math::Round qw/round_rand/;

# Messbereich (Anzahl der Messungen);
my $von = 1;
my $bis  = 10_000;

# Die Zahl, die gemessen/geprüft wird
my $zahl = 2.5;

# Zaehler fuer Anzahl der Messungen
my $anzahl_messungen = 0;

# Container fuer die Messergebnisse
my %messergebnis = ();

for ( $von .. $bis ) {
	my $gerundet = round_rand($zahl);
	$messergebnis{$gerundet}++;
	$anzahl_messungen++;
}

my @messwerte = keys %messergebnis;

foreach my $messwert (@messwerte) {
	print "$zahl wurde $messergebnis{$messwert} mal zu $messwert gerundet.\n";
	print "Das entspricht ", $messergebnis{$messwert} / $anzahl_messungen * 100 , "%.\n";
}

Das Programm erzeugt folgende Ausgabe:

2.5 wurde 5044 mal zu 3 gerundet.
Das entspricht 50.44%.
2.5 wurde 4956 mal zu 2 gerundet.
Das entspricht 49.56%.

Runden auf Ganzzahlen im Vergleich

Das folgende Beispiel erstellt eine kleine tabellarische Übersicht, die den Vergleich der vorgestellten Prozeduren vereinfachen soll.

#!/usr/bin/perl 
use strict;
use warnings;


use Math::Round qw/round round_even round_odd round_rand/;


my @zahlen = qw /-2.5 -2.0 -1.5 -1.0 -0.5 
                 0.0 
                 0.5 1.0 1.5 2.0 2.5
                /;

# Format für printf
my $format = '%6s %6s %10s %10s %10s';


printf("$format\n", 'Zahl', 'round', 'round_even', 'round_odd', 'round_rand');

foreach my $zahl ( @zahlen ) {
	printf("$format\n", $zahl, round($zahl), round_even($zahl), round_odd($zahl), round_rand($zahl) );
}

Das Programm erzeugt folgende Ausgabe:

  Zahl  round round_even  round_odd round_rand
  -2.5     -3         -2         -3         -3
  -2.0     -2         -2         -2         -2
  -1.5     -2         -2         -1         -2
  -1.0     -1         -1         -1         -1
  -0.5     -1          0         -1          0
   0.0      0          0          0          0
   0.5      1          0          1          1
   1.0      1          1          1          1
   1.5      2          2          1          2
   2.0      2          2          2          2
   2.5      3          2          3          2

Auf Fließkommazahlen runden

Hier bietet sich nearest(TARGET, LIST) an. nearest rundet zum Vielfachen von TARGET auf bzw. ab.

Beispiel: Auf eine bzw. zwei Nachkommastellen runden

#!/usr/bin/perl 
use strict;
use warnings;

use Math::Round qw/nearest/;

my @zahlen = qw /-2.5 -2.0 -1.5 -1.0 -0.5 
                 0.0 
                 0.5 1.0 1.5 2.0 2.5
                 125.3456 125.5678/;

my $format = '%8s %8s %8s';
printf("$format\n", 'Zahl', 'Eine NK', 'Zwei NK' );
foreach my $zahl ( @zahlen ) {
			# Eine Nachkommastelle (0.1)
			# Zwei Nachkommastellen (0.01)
	printf("$format\n", $zahl,  nearest('0.1',$zahl), nearest('0.01',$zahl) );
}

Das Programm erzeugt folgende Ausgabe:

    Zahl  Eine NK  Zwei NK
    -2.5     -2.5     -2.5
    -2.0       -2       -2
    -1.5     -1.5     -1.5
    -1.0       -1       -1
    -0.5     -0.5     -0.5
     0.0        0        0
     0.5      0.5      0.5
     1.0        1        1
     1.5      1.5      1.5
     2.0        2        2
     2.5      2.5      2.5
125.3456    125.3   125.35
125.5678    125.6   125.57

Zum nächsten Vielfachen auf- oder abrunden

Die beiden Prozeduren nlowmult und nhimult runden zum nächsten kleineren bzw. größerem Vielfachen ab bzw. auf.

  • nlowmult TARGET, LIST rundet zum nächsten kleineren Vielfachen ab.
  • nhimult TARGET, LIST rundet zum nächsten größeren Vielfachen auf.

Beispiel: Auf volle 100er auf- bzw. abrunden.

#!/usr/bin/perl 
use strict;
use warnings;

use Math::Round qw/nlowmult nhimult/;

my @zahlen = qw/ 126 176/;

# Auf volle 100er auf- bzw. abrunden
my $target = 100;

my $format = '%6s %10s %10s';

printf("$format\n", 'Zahl', 'nlowmult', 'nhimult');

foreach my $zahl (@zahlen) {
	printf("$format\n", $zahl, nlowmult($target,$zahl), nhimult($target,$zahl) );
}

Das Programm erzeugt folgende Ausgabe:

  Zahl   nlowmult    nhimult
   126        100        200
   176        100        200

Das funktioniert auch mit Vielfachen von Fließkommazahlen. Probieren Sie es einfach mal aus.

Siehe auch

Über dieses Archiv

Diese Seite enthält alle Einträge von Perl HowTo von neu nach alt.

September 2008 ist das vorherige Archiv.

Dezember 2008 ist das nächste Archiv.

Aktuelle Einträge finden Sie auf der Startseite, alle Einträge in den Archiven.

Blog Roll

Powered by

Powered by Movable Type 5.2.10

Creative Commons-Lizenz

Creative Commons License
Dieses Weblog steht unter einer Creative Commons-Lizenz.