Wie tun Sie Runden Sie eine Fließkommazahl in Perl?
-
05-07-2019 - |
Frage
Wie kann ich Runden Sie eine Dezimalzahl (Gleitkommazahl) auf die nächste ganze Zahl?
z.B.
1.2 = 1
1.7 = 2
Lösung
Leistung von perldoc -q round
Macht Perl haben eine Funktion round ()?Was ist ceil() und floor()?Trig-Funktionen?Denken Sie daran, dass
int()
lediglich schneidet in Richtung0
.Für die Rundung auf eine bestimmte Anzahl von Ziffern,sprintf()
oderprintf()
ist in der Regel die einfachste route.
printf("%.3f", 3.1415926535); # prints 3.142
Die
POSIX
Modul - (Teil der standard-Perl-distribution) implementiertceil()
,floor()
, und eine Reihe von anderen mathematischen und trigonometrischen Funktionen.
use POSIX; $ceil = ceil(3.5); # 4 $floor = floor(3.5); # 3
In 5.000 bis 5.003 perls, Trigonometrie gemacht wurde
Math::Complex
Modul.Mit 5.004, dieMath::Trig
Modul - (Teil der standard-Perl Verteilung) führt die trigonometrischen Funktionen.Intern verwendet dieMath::Complex
Modul, und einige Funktionen kann ausbrechen aus dem reellen Achse in der komplexen Ebene, zum Beispiel den inversen Sinus von 2.Rundung in Finanz-Anwendungen können schwerwiegende Folgen haben, und die Rundungsmethode verwendet, sollte genau angegeben werden.In diese Fällen, wahrscheinlich lohnt es sich nicht zu trauen, was die Rundung wird verwendet von Perl, sondern stattdessen implementieren Sie die Runden-Funktion, die Sie benötigen selbst.
Um zu sehen, warum, zu bemerken, wie Sie immer noch ein Problem haben, auf halbem Weg-Punkt Wechsel:
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
Nicht die Schuld Perl.Es ist die gleiche wie in C.IEEE sagt, wir haben zu tun diese.Perl zahlen, deren absolute Werte sind Ganzzahlen unter
2**31
(auf 32-bit-Maschinen) funktionieren so ziemlich wie mathematische Ganzzahlen.Andere zahlen sind nicht garantiert werden.
Andere Tipps
Während die komplexen Antworten etwa auf halbem Weg Marken nicht andere Meinung und so weiter, für die häufiger (und möglicherweise trivial) Use-Case:
my $rounded = int($float + 0.5);
UPDATE
Wenn es möglich ist, für Ihre $float
negativ zu sein, die folgende Variante wird das richtige Ergebnis produzieren:
my $rounded = int($float + $float/abs($float*2 || 1));
Mit dieser Berechnung -1,4 auf -1 gerundet und -1,6 bis -2 und Null wird nicht explodieren.
Sie können entweder ein Modul wie verwenden Math :: Round :
use Math::Round;
my $rounded = round( $float );
Oder Sie können es die rohe Art und Weise tun:
my $rounded = sprintf "%.0f", $float;
Wenn Sie sich entscheiden printf oder sprintf zu verwenden, beachten Sie, dass sie die Rund die Hälfte sogar Verfahren.
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
Siehe perldoc / perlfaq :
Beachten Sie, dass
int()
lediglich in Richtung 0. kürzt, um eine für die Rundung bestimmte Anzahl von Ziffern,sprintf()
oderprintf()
ist in der Regel der einfachste Weg.printf("%.3f",3.1415926535); # prints 3.142
Das
POSIX
Modul (Teil der Standard-Perl-Distribution) implementiertceil()
,floor()
, und eine Reihe von anderen mathematischen und trigonometrische Funktionen.use POSIX; $ceil = ceil(3.5); # 4 $floor = floor(3.5); # 3
In 5,000-5,003 perls, Trigonometrie im
Math::Complex
Modul durchgeführt wurde.5.004, das
Math::Trig
Modul (Teil der Standard-Perl-Verteilung)> implementiert die trigonometrischen Funktionen.Intern verwendet es die
Math::Complex
Modul und einige Funktionen können brechen aus der realen Achse in der komplexen Ebene, beispielsweise dem inversen Sinus von 2.Rounding in Finanzanwendungen können schwerwiegende Folgen haben, und die Rundung Verfahren verwendet wird, sollte genau festgelegt werden. In diesen Fällen lohnt es sich wahrscheinlich nicht um Vertrauen Welches System Abrundung wird von Perl verwendet wird, sondern stattdessen implementieren die Rundungsfunktion müssen Sie sich selbst.
Um zu sehen, warum darauf, wie Sie immer noch ein Problem auf halben Weg-Punkt-Wechsel haben werden:
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
Sie nicht Perl schuld. Es ist das gleiche wie in C. IEEE sagt, wir haben zu tun diese. Perl Zahlen, deren absolute Werte sind ganze Zahlen unter 2 ** 31 (auf 32-Bit-Maschinen) wird ziemlich viel wie mathematische Zahlen arbeiten. Andere Zahlen sind nicht garantiert.
Sie benötigen keine externe Modul.
$x[0] = 1.2;
$x[1] = 1.7;
foreach (@x){
print $_.' = '.( ( ($_-int($_))<0.5) ? int($_) : int($_)+1 );
print "\n";
}
Ich fehlen möglicherweise Ihren Standpunkt, aber ich dachte, das war viel sauberer Weg, um die gleiche Arbeit.
Was dieses tut, ist ein Spaziergang durch jede positive Zahl in das element, drucken Sie die Anzahl und die abgerundete ganze Zahl in dem format, das Sie erwähnt.Der code verkettet jeweiligen gerundet positive ganze Zahl ist nur auf der Grundlage der Dezimalstellen.int($_) grundsätzlich Runde-down die Zahl so ($-int($)) erfasst die Nachkommastellen.Wenn die Dezimalstellen sind (per definition) strikt kleiner als 0,5, rund-um-die Nummer.Wenn nicht, round-up durch hinzufügen 1.
Im Folgenden wird rund um positive oder negative Zahlen zu einem bestimmten Dezimalstelle:
sub round ()
{
my ($x, $pow10) = @_;
my $a = 10 ** $pow10;
return (int($x / $a + (($x < 0) ? -0.5 : 0.5)) * $a);
}
Es folgt eine Stichprobe von fünf verschiedenen Arten Werte zu summieren. Die erste ist eine naive Weise die Summation (und nicht) durchzuführen. Der zweite versucht sprintf()
zu verwenden, aber es scheitert auch. Die dritte Einsatz sprintf()
erfolgreich, während die beiden letzten (4. und 5.) floor($value + 0.5)
verwenden.
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
Beachten Sie, dass floor($value + 0.5)
mit int($value + 0.5)
ersetzt werden, um die Abhängigkeit von POSIX
zu entfernen.
Negative Zahlen können einige Macken hinzufügen, dass die Menschen sich bewusst sein müssen.
printf
-Stil Ansätze geben uns richtige Zahlen, aber sie können in einigen ungeradeen Displays führen. Wir haben herausgefunden, dass dieses Verfahren (meiner Meinung nach, dummerweise) bringt ein -
Zeichen, ob es soll oder nicht. Zum Beispiel -0,01 gerundet auf eine Dezimalstelle gibt ein -0,0, anstatt nur 0. Wenn Sie gehen, um den printf
Stil Ansatz zu tun, und Sie wissen, Sie wollen keine Nachkommastelle, Verwendung %d
und nicht %f
(wenn Sie Dezimalzahlen benötigen, es ist, wenn das Display wackelig wird).
Während es richtig und für Mathematik ist keine große Sache, für die Anzeige es einfach seltsam sieht so etwas wie „-0,0“ zeigt.
Für die int Methode können negative Zahlen ändern, was Sie als Ergebnis wollen (obwohl es einige Argumente, die gemacht werden können, sie korrekt sind).
Die int + 0.5
verursacht echte Probleme mit -negativen Zahlen, wenn Sie es wünschen, so zu arbeiten, aber ich glaube, die meisten Menschen nicht. -0,9 sollte wohl rund -1, nicht 0. Wenn Sie wissen, dass Sie eine negative wollen eine Decke sein, anstatt ein Boden, dann können Sie es in Einzeiler tun, sonst könnten Sie die int-Methode mit einem Minderjährigen verwenden Modifikation (das funktioniert natürlich nur, wieder ganze Zahlen zu erhalten:
my $var = -9.1;
my $tmpRounded = int( abs($var) + 0.5));
my $finalRounded = $var >= 0 ? 0 + $tmpRounded : 0 - $tmpRounded;
Meine Lösung für 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 );
Wenn Sie nur besorgt sind mit immer einen ganzzahligen Wert aus einer ganzen Gleitpunktzahl (d 12347,9999 oder 54321,0001), diesen Ansatz (entlehnt und modifizierte von oben) wird es tun:
my $rounded = floor($float + 0.1);
cat table |
perl -ne '/\d+\s+(\d+)\s+(\S+)/ && print "".**int**(log($1)/log(2))."\t$2\n";'