Readers: 27
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
sub kfmrunden { # # Aufruf: # ======= # my $gerundet = &kfmrunden([wert],[stellen]); # [wert] = Nur nummerische Werte! Keine Extremwerte wie z.B. 1e+100 oder 4.24306121591708e-007! Kein Komma, kein Tausenderpunkt etc. # [stellen] = Anzahl gewünschte Nachkommastellen, zwischen 0 und 9 zulässig # my $wert = shift (@_) || 0; my $stellen = shift (@_) || 0; my $vorzeichen = ''; my $vorkomma = 0; my $nachkomma = ''; if ($stellen >= 0 && $stellen <= 9 && !($wert =~ /[^+-\.0-9]/)) { if (substr ($wert,0,1) eq '-' || substr ($wert,0,1) eq '+') { $vorzeichen = '-' if substr ($wert,0,1) eq '-'; $wert = substr ($wert,1); } ($vorkomma,$nachkomma) = split /\./,$wert; $vorkomma = 0 unless $vorkomma; $nachkomma = '' unless $nachkomma; if (length ($nachkomma) > $stellen) { my $auf = (substr ($nachkomma,$stellen,1) > 4 ? 1 : 0); if (!$stellen) { $vorkomma ++ if $auf; $nachkomma = ''; } else { if ($auf) { $nachkomma = substr ($nachkomma,0,$stellen) + 1; if (length ($nachkomma) > $stellen) { $vorkomma ++; $nachkomma = ''; } else { $nachkomma = substr ($nachkomma,0,$stellen); } } else { $nachkomma = substr ($nachkomma,0,$stellen); } } } } return "$vorzeichen$vorkomma" . ($nachkomma ne '' ? ".$nachkomma" : ''); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
my $sub = 'kfmrunden'; my %test = ( (.57 * 100) . ',2' => 57, '1.295,2' => 1.3, '9.999,3' => 9.999, '9.999,2' => 10, '9.999,1' => 10, '9.999,0' => 10, '8.9964,2' => 9, '8.9964,0' => 9, '8.9964,1' => 9, '1,2' => 1, '1.455,2' => 1.46, '1.9,0' => 2, '12345.34,1' => 12345.3, '12345.35,1' => 12345.4, '12.2345678905,9' => 12.234567891, '.5678,3' => .568, '4.24306121591708e-007,2' => 0, '1e+100,3' => 0, '.5674,3' => .567, '.5670,3' => .567, '456.4,0' => 456, '456.5,0' => 457, '0.49999999,0' => 0, '0.999999999,0' => 1, '0.5,0' => 1, '999999999999.999,2' => 1000000000000, '999999999999.994,2' => 999999999999.99, '00000034.999,2' => 35, ); my $space = '.' x 25; foreach my $test (sort {(split /,/,$a)[0] <=> (split /,/,$b)[0] } keys %test) { foreach my $multi (1,-1) { my ($wert,$stellen) = split /,/,$test; $wert = $wert * $multi; my $erwartet = $test{$test} * $multi; my $gerundet = &{$sub} ($wert,$stellen); print "\'$wert\'" . substr ($space,0,25 - length ($wert)) . "auf $stellen Stelle(n): \'$gerundet\'" . substr ($space,0,25 - length ($gerundet)) . ($gerundet == $erwartet ? ' OK' : " FEHLER! Erwartet \'$erwartet\'") . "\n"; } }
2009-12-11T09:47:58 Dubu(0.005,2) => 0.1?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
sub kfmrunden { # # Aufruf: # ======= # my $gerundet = &kfmrunden([wert],[stellen]); # [wert] = Nur nummerische Werte! Keine Extremwerte wie z.B. 1e+100 oder 4.24306121591708e-007! Kein Komma, kein Tausenderpunkt etc. # [stellen] = Anzahl gewünschte Nachkommastellen, zwischen 0 und 9 zulässig # my $wert = shift (@_) || 0; my $stellen = shift (@_) || 0; my $vorzeichen = ''; my $vorkomma = 0; my $nachkomma = ''; if ($stellen >= 0 && $stellen <= 9 && !($wert =~ /[^+-\.0-9]/)) { if (substr ($wert,0,1) eq '-' || substr ($wert,0,1) eq '+') { $vorzeichen = '-' if substr ($wert,0,1) eq '-'; $wert = substr ($wert,1); } ($vorkomma,$nachkomma) = split /\./,$wert; $vorkomma = 0 unless $vorkomma; $nachkomma = '' unless $nachkomma; if (length ($nachkomma) > $stellen) { my $auf = (substr ($nachkomma,$stellen,1) > 4 ? 1 : 0); if (!$stellen) { $vorkomma ++ if $auf; $nachkomma = ''; } else { if ($auf) { my $vornull = 0; for (my $z = 0; $z < $stellen; $z ++) { last if substr ($nachkomma,$z,1) ne '0'; $vornull ++; } if (substr ($nachkomma,0,$stellen) eq '9' x $stellen) { $vorkomma ++; $nachkomma = ''; } else { $vornull -- if substr ($nachkomma,$stellen - 1,1) eq '0'; my $temp = substr ($nachkomma,0,$stellen) + 1; $nachkomma = ($vornull ? '0' x $vornull : '') . $temp; } } else { $nachkomma = substr ($nachkomma,0,$stellen); } } } } return ("$vorkomma.$nachkomma" / 1 ? $vorzeichen : '') . "$vorkomma" . ($nachkomma ne '' && substr ($nachkomma,0,$stellen) ne '0' x $stellen ? ".$nachkomma" : ''); }
1 2 3 4
for (my $z = 0; $z < $stellen; $z ++) { last if substr ($nachkomma,$z,1) ne '0'; $vornull ++; }
1 2
my ($count) = $nachkomma =~ m!^(0*)[^0]!; $vornull = length $count > $stellen ? $stellen : length $count;
1
2
3
4
~/community$ perl bianca.pl
Rate bianca neu
bianca 300300/s -- -74%
neu 1136364/s 278% --
2009-12-11T14:46:55 reneeCode: (dl )1
2
3
4~/community$ perl bianca.pl
Rate bianca neu
bianca 300300/s -- -74%
neu 1136364/s 278% --
2009-12-11T14:46:55 reneeCode (perl): (dl )my ($count) = $nachkomma =~ m!^(0*)[^0]!;
2009-12-11T16:14:46 reneeLass mal das "[^0]" im Regulären Ausdruck weg...
2009-12-11T14:46:55 reneeCode (perl): (dl )1 2my ($count) = $nachkomma =~ m!^(0*)[^0]!; $vornull = length $count > $stellen ? $stellen : length $count;
1 2 3 4
my ($count) = $nachkomma =~ m!^(0*)[^0]!; $count = '' if !defined $count; my $vornull = length $count; $vornull -- if substr ($nachkomma,0,$stellen) eq '0' x $stellen;
'1e+100,3' => 0,
Guest werCode: (dl )'1e+100,3' => 0,
Soll das nicht eher "-" sein?
Guest werdann kann der wert nach dem runden aber nicht 0 sein.
1 2
return 0 if($wert>=1e+100 or $wert<=1e-007); return 0 if($stellen<0 or $stellen>9);
Guest werIn den Fällen währe es möglicherweise besser undef zurück zu liefern um dem Aufrufer mit zu teilen, dass etwas nicht funktioniert hat.
$wert =~ /[^+-\.0-9]/
$vorkomma = substr ($vorkomma,1) while $vorkomma =~ /^0/ && length ($vorkomma) > 1;
$vorkomma =~ s/^0+([^0])/$1/;
2009-12-11T16:31:03 pqCode (perl): (dl )$vorkomma =~ s/^0+([^0])/$1/;
2009-12-11T16:41:18 biancaEdit: Mir fällt gerade auf, dass das Forum pq groß schreibt, wenn man Dich zitiert. Ist das egal oder case sensitive?
2009-12-11T16:45:50 pqdas macht nicht "das forum", sondern das ist CSS. und das passiert bei *jedem* user, und das auch schon immer.
eingebaut hat das betterworld.
my $rounded = int( $num * (10 ** $stellen) + 0.5 ) / 10 ** $stellen;
2009-12-11T17:46:12 pqwieso multiplizierst du eigentlich nicht vor dem runden mit 10 ^ stellenanzahl, rundest dann und teilst dann wieder?
1 2 3 4 5 6 7 8 9 10 11 12 13 14
sub pq { my $num = shift (@_) || 0; my $stellen = shift (@_) || 0; my $vorzeichen = ''; my $rounded = '0'; if ($stellen >= 0 && $stellen <= 9 && !($num =~ /[^+-\.0-9]/)) { if (substr ($num,0,1) eq '-' || substr ($num,0,1) eq '+') { $vorzeichen = '-' if substr ($num,0,1) eq '-'; $num = substr ($num,1); } $rounded = int( $num * (10 ** $stellen) + 0.5 ) / 10 ** $stellen; } return ($rounded ne '0' ? $vorzeichen : '') . $rounded; }
2009-12-11T18:35:22 pqdas mit dem vorzeichen machst du wahnsinnig kompliziert. wieso selber das minus abtrennen (und dann auch noch mit substr, aua, du bist echt substr und index fan...). einfach wenn $num < 0, 0.5 *abziehen*, wenn $num > 0, 0.5 *addieren*. ist doch ganz simpel.
2009-12-11T18:35:22 pqich möchte eine sub, die derart kompliziert und unnötigerweise mit substr jongliert, nicht mit meinem nickname verbunden sehn...
1 2 3 4 5 6 7 8 9
sub neueloesung { my $num = shift (@_) || 0; my $stellen = shift (@_) || 0; my $rounded = '0'; if ($stellen >= 0 && $stellen <= 9 && !($num =~ /[^-\.0-9]/)) { $rounded = int ($num * (10 ** $stellen) + ($num < 0 ? -0.5 : 0.5) ) / 10 ** $stellen; } return $rounded; }
0.055 -> 0.05499999999999999334 dein Erbenis 0.05
2009-12-11T20:23:16 topegDein Code macht aber die klassischen Fehler wie sie beim runden mit "int" auftreten:
Code: (dl )0.055 -> 0.05499999999999999334 dein Erbenis 0.05
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
my %test = ( '0.05499999999999999334,3' => '0.055', '-0.05499999999999999334,3' => '-0.055', '0.05499999999999999334,2' => '0.05', '-0.05499999999999999334,2' => '-0.05', '0.055,2' => '0.06', '-0.055,2' => '-0.06', '2132343.565,2' => '2132343.57', '-2132343.565,2' => '-2132343.57', '0.2225000000,3' => '0.223', '-0.2225000000,3' => '-0.223', '0.2224000000,3' => '0.222', '-0.2224000000,3' => '-0.222', '0.05,1' => '0.1', '-0.05,1' => '-0.1', '0.005,2' => '0.01', '-0.005,2' => '-0.01', '0.1005,3' => '0.101', '-0.1005,3' => '-0.101', '0.0105,3' => '0.011', '-0.0105,3' => '-0.011', '0.105,2' => '0.11', '-0.105,2' => '-0.11', '0.04,1' => '0', '-0.04,1' => '0', '0.004,2' => '0', '-0.004,2' => '0', (.57 * 100) . ',2' => '57', (-.57 * 100) . ',2' => '-57', '1.295,2' => '1.3', '-1.295,2' => '-1.3', '9.999,3' => '9.999', '-9.999,3' => '-9.999', '9.999,2' => '10', '-9.999,2' => '-10', '9.999,1' => '10', '-9.999,1' => '-10', '9.999,0' => '10', '-9.999,0' => '-10', '8.9964,2' => '9', '-8.9964,2' => '-9', '8.9964,0' => '9', '-8.9964,0' => '-9', '8.9964,1' => '9', '-8.9964,1' => '-9', '1,2' => '1', '-1,2' => '-1', '1.455,2' => '1.46', '-1.455,2' => '-1.46', '1.9,0' => '2', '-1.9,0' => '-2', '12345.34,1' => '12345.3', '-12345.34,1' => '-12345.3', '12345.35,1' => '12345.4', '-12345.35,1' => '-12345.4', '12.2345678905,9' => '12.234567891', '-12.2345678905,9' => '-12.234567891', '.5678,3' => '0.568', '-.5678,3' => '-0.568', '4.24306121591708e-007,2' => '0', '1e+100,3' => '1e+100', '.5674,3' => '0.567', '-.5674,3' => '-0.567', '.5670,3' => '0.567', '-.5670,3' => '-0.567', '456.4,0' => '456', '-456.4,0' => '-456', '456.5,0' => '457', '-456.5,0' => '-457', '0.49999999,0' => '0', '-0.49999999,0' => '0', '0.999999999,0' => '1', '-0.999999999,0' => '-1', '0.5,0' => '1', '-0.5,0' => '-1', '999999999999.999,2' => '1000000000000', '-999999999999.999,2' => '-1000000000000', '999999999999.994,2' => '999999999999.99', '-999999999999.994,2' => '-999999999999.99', '00000034.999,2' => '35', '-00000034.999,2' => '-35', ); my $space = '.' x 25; foreach my $test (sort {(split /,/,$a)[0] <=> (split /,/,$b)[0] } keys %test) { my ($wert,$stellen) = split /,/,$test; my $gerundet = &{$sub} ($wert,$stellen); print "\'$wert\'" . substr ($space,0,25 - length ($wert)) . "auf $stellen Stelle(n): \'$gerundet\'" . substr ($space,0,25 - length ($gerundet)) . ($gerundet eq $test{$test} ? ' OK' : " FEHLER! Erwartet \'$test{$test}\'") . "\n"; } print "(7.56*1.19)................auf 2 Stellen : \'" . &{$sub} (7.56*1.19,2) . "\'........................ " . (&{$sub} (7.56*1.19,2) == 9 ? 'OK' : 'FEHLER! Erwartet \'9\'') . "\n"; print "(-7.56*1.19)...............auf 2 Stellen : \'" . &{$sub} (-7.56*1.19,2) . "\'....................... " . (&{$sub} (-7.56*1.19,2) == -9 ? 'OK' : 'FEHLER! Erwartet \'9\'') . "\n"; print "(.57*100)..................auf 0 Stellen : \'" . &{$sub} (.57*100,0) . "\'....................... " . (&{$sub} (.57*100,0) == 57 ? 'OK' : 'FEHLER! Erwartet \'57\'') . "\n"; print "(.57*100)..................auf 2 Stellen : \'" . &{$sub} (.57*100,2) . "\'....................... " . (&{$sub} (.57*100,2) == 57 ? 'OK' : 'FEHLER! Erwartet \'57\'') . "\n";
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
#!/usr/bin/perl use strict; use warnings; ######################################################################################### ######################################################################################### sub kfmrunden_topeg { my $zahl=shift; return undef unless(defined($zahl)); my $stelle=shift || 0; # prepare for Xe[+-]Y Notation # handle big float my ($pre,$number,$exponent)=$zahl=~/^([-+]?)([\d.]+)(?:e([+-]?\d+))?$/; $exponent=0 unless defined($exponent); $pre='' if(!$pre || $pre ne '-'); # full integer number with exponent my ($l)=$number=~/\.(.+)$/; $exponent-=$l?length($l):0; $number=~y/.//d; # correct position for round my $stellen=$stelle+$exponent; if($stellen<0) { $exponent=-$stelle; my $add=substr(('0'x abs($stellen)).$number,$stellen,-$stellen); # round... $number+=$add; $number=('0'x abs($stellen)).sprintf("%.0f",$number); substr($number,$stellen,-$stellen,''); } # reconstruct number $zahl="$pre${number}e$exponent"; #$zahl=sprintf("%.${stelle}f",$zahl); return $zahl+0; } sub neueloesung { my $num = shift (@_) || 0; my $stellen = shift (@_) || 0; my $rounded = '0'; if ($stellen >= 0 && $stellen <= 9 && !($num =~ /[^-\.0-9]/)) { $rounded = int ($num * (10 ** $stellen) + ($num < 0 ? -0.5 : 0.5) ) / 10 ** $stellen; } return $rounded; } ######################################################################################### ######################################################################################### my @subs=('kfmrunden_topeg','neueloesung'); ######################################################################################### ######################################################################################### sub tests { my $sub=shift; $sub=\&$sub; my $cnt=0; for my $i (0..200) { my $stellen=9; my $wert="0.".substr(("0"x($stellen+1)).($i*5),-($stellen+1),($stellen+1)); $cnt++ if(($i*5)%10); my $erwartet = "0.".substr(("0"x($stellen+1)).($cnt*10),-($stellen+1),($stellen+1)) +0; my $gerundet=$sub->($wert,$stellen); printout($wert,$erwartet,$gerundet,$stellen); }; print "#"x80,"\n"; $cnt=1; my $erwartet=0.01; my $wert=0.005; for my $i (0..200) { my $stellen=2; my $gerundet=$sub->($wert,$stellen); printout($wert,$erwartet,$gerundet,$stellen); $wert+=0.005; if($cnt==2) { $erwartet+=0.01; $cnt=0; } $cnt++; }; print "#"x80,"\n"; } sub printout { my $wert=shift; my $soll=shift; my $ist=shift; my $stellen=shift; $soll=sprintf("%0.${stellen}f",$soll); $ist=sprintf("%0.${stellen}f",$ist); my $space = '.' x 25; print "\'$wert\'" . substr ($space,0,25 - length ($wert)) . "auf $stellen Stelle(n): \'$ist\'" . substr ($space,0,25 - length ($ist)) . ($soll == $ist ? ' OK' : " FEHLER! Erwartet \'$soll\'") . "\n"; } ######################################################################################### ######################################################################################### for my $sub (@subs) { print "SUB $sub\n"; tests($sub); print "#"x80,"\n"; print "#"x80,"\n"; print "#"x80,"\n"; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
SUB neueloesung
'0.0000000000'.............auf 9 Stelle(n): '0.000000000'.............. OK
'0.0000000005'.............auf 9 Stelle(n): '0.000000001'.............. OK
'0.0000000010'.............auf 9 Stelle(n): '0.000000001'.............. OK
'0.0000000015'.............auf 9 Stelle(n): '0.000000002'.............. OK
'0.0000000020'.............auf 9 Stelle(n): '0.000000002'.............. OK
'0.0000000025'.............auf 9 Stelle(n): '0.000000003'.............. OK
'0.0000000030'.............auf 9 Stelle(n): '0.000000003'.............. OK
'0.0000000035'.............auf 9 Stelle(n): '0.000000004'.............. OK
'0.0000000040'.............auf 9 Stelle(n): '0.000000004'.............. OK
'0.0000000045'.............auf 9 Stelle(n): '0.000000005'.............. OK
'0.0000000050'.............auf 9 Stelle(n): '0.000000005'.............. OK
'0.0000000055'.............auf 9 Stelle(n): '0.000000006'.............. OK
'0.0000000060'.............auf 9 Stelle(n): '0.000000006'.............. OK
'0.0000000065'.............auf 9 Stelle(n): '0.000000007'.............. OK
'0.0000000070'.............auf 9 Stelle(n): '0.000000007'.............. OK
'0.0000000075'.............auf 9 Stelle(n): '0.000000007'.............. FEHLER! Erwartet '0.000000008'
'0.0000000080'.............auf 9 Stelle(n): '0.000000008'.............. OK
'0.0000000085'.............auf 9 Stelle(n): '0.000000009'.............. OK
...
'0.0000000305'.............auf 9 Stelle(n): '0.000000031'.............. OK
'0.0000000310'.............auf 9 Stelle(n): '0.000000031'.............. OK
'0.0000000315'.............auf 9 Stelle(n): '0.000000031'.............. FEHLER! Erwartet '0.000000032'
'0.0000000320'.............auf 9 Stelle(n): '0.000000032'.............. OK
'0.0000000325'.............auf 9 Stelle(n): '0.000000033'.............. OK
...
################################################################################
'0.005'....................auf 2 Stelle(n): '0.01'..................... OK
'0.01'.....................auf 2 Stelle(n): '0.01'..................... OK
'0.015'....................auf 2 Stelle(n): '0.02'..................... OK
'0.02'.....................auf 2 Stelle(n): '0.02'..................... OK
'0.025'....................auf 2 Stelle(n): '0.03'..................... OK
'0.03'.....................auf 2 Stelle(n): '0.03'..................... OK
'0.035'....................auf 2 Stelle(n): '0.04'..................... OK
'0.04'.....................auf 2 Stelle(n): '0.04'..................... OK
'0.045'....................auf 2 Stelle(n): '0.05'..................... OK
'0.05'.....................auf 2 Stelle(n): '0.05'..................... OK
'0.055'....................auf 2 Stelle(n): '0.05'..................... FEHLER! Erwartet '0.06'
'0.06'.....................auf 2 Stelle(n): '0.06'..................... OK
'0.065'....................auf 2 Stelle(n): '0.06'..................... FEHLER! Erwartet '0.07'
'0.07'.....................auf 2 Stelle(n): '0.07'..................... OK
...
2009-12-12T00:40:51 topegUm das etwas zu illustrieren:
1 2 3 4 5 6 7 8 9 10 11 12
sub kfmrunden { # # Aufruf: # ======= # my $gerundet = &kfmrunden ([wert],[stellen]); # [wert] = Nur nummerische Werte! Kein Komma, kein Tausenderpunkt etc. # [stellen] = Anzahl gewünschte Nachkommastellen, Werte >= 0 zulässig # my $num = shift (@_) || 0; my $stellen = shift (@_) || 0; return int ($num * (10 ** $stellen) + ($num < 0 ? -0.5 : 0.5) ) / 10 ** $stellen; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
'-999999999999.999'........auf 2 Stelle(n): '-1000000000000'........... OK
'-999999999999.994'........auf 2 Stelle(n): '-999999999999.99'......... OK
'-2132343.565'.............auf 2 Stelle(n): '-2132343.57'.............. OK
'-12345.35'................auf 1 Stelle(n): '-12345.4'................. OK
'-12345.34'................auf 1 Stelle(n): '-12345.3'................. OK
'-456.5'...................auf 0 Stelle(n): '-457'..................... OK
'-456.4'...................auf 0 Stelle(n): '-456'..................... OK
'-57'......................auf 2 Stelle(n): '-57'...................... OK
'-00000034.999'............auf 2 Stelle(n): '-35'...................... OK
'-12.2345678905'...........auf 9 Stelle(n): '-12.234567891'............ OK
'-9.999'...................auf 3 Stelle(n): '-9.999'................... OK
'-9.999'...................auf 0 Stelle(n): '-10'...................... OK
'-9.999'...................auf 1 Stelle(n): '-10'...................... OK
'-9.999'...................auf 2 Stelle(n): '-10'...................... OK
'-8.9964'..................auf 2 Stelle(n): '-9'....................... OK
'-8.9964'..................auf 0 Stelle(n): '-9'....................... OK
'-8.9964'..................auf 1 Stelle(n): '-9'....................... OK
'-1.9'.....................auf 0 Stelle(n): '-2'....................... OK
'-1.455'...................auf 2 Stelle(n): '-1.46'.................... OK
'-1.295'...................auf 2 Stelle(n): '-1.3'..................... OK
'-1'.......................auf 2 Stelle(n): '-1'....................... OK
'-0.999999999'.............auf 0 Stelle(n): '-1'....................... OK
'-.5678'...................auf 3 Stelle(n): '-0.568'................... OK
'-.5674'...................auf 3 Stelle(n): '-0.567'................... OK
'-.5670'...................auf 3 Stelle(n): '-0.567'................... OK
'-0.5'.....................auf 0 Stelle(n): '-1'....................... OK
'-0.49999999'..............auf 0 Stelle(n): '0'........................ OK
'-0.2225000000'............auf 3 Stelle(n): '-0.223'................... OK
'-0.2224000000'............auf 3 Stelle(n): '-0.222'................... OK
'-0.105'...................auf 2 Stelle(n): '-0.11'.................... OK
'-0.1005'..................auf 3 Stelle(n): '-0.101'................... OK
'-0.055'...................auf 2 Stelle(n): '-0.06'.................... OK
'-0.05499999999999999334'..auf 2 Stelle(n): '-0.05'.................... OK
'-0.05499999999999999334'..auf 3 Stelle(n): '-0.055'................... OK
'-0.05'....................auf 1 Stelle(n): '-0.1'..................... OK
'-0.04'....................auf 1 Stelle(n): '0'........................ OK
'-0.0105'..................auf 3 Stelle(n): '-0.011'................... OK
'-0.005'...................auf 2 Stelle(n): '-0.01'.................... OK
'-0.004'...................auf 2 Stelle(n): '0'........................ OK
'-0.0000000075'............auf 9 Stelle(n): '-7e-009'.................. FEHLER! Erwartet '-0.000000008'
'0.0000000075'.............auf 9 Stelle(n): '7e-009'................... FEHLER! Erwartet '0.000000008'
'4.24306121591708e-007'....auf 2 Stelle(n): '0'........................ OK
'0.004'....................auf 2 Stelle(n): '0'........................ OK
'0.005'....................auf 2 Stelle(n): '0.01'..................... OK
'0.0105'...................auf 3 Stelle(n): '0.011'.................... OK
'0.04'.....................auf 1 Stelle(n): '0'........................ OK
'0.05'.....................auf 1 Stelle(n): '0.1'...................... OK
'0.05499999999999999334'...auf 2 Stelle(n): '0.05'..................... OK
'0.05499999999999999334'...auf 3 Stelle(n): '0.055'.................... OK
'0.055'....................auf 2 Stelle(n): '0.06'..................... OK
'0.1005'...................auf 3 Stelle(n): '0.101'.................... OK
'0.105'....................auf 2 Stelle(n): '0.11'..................... OK
'0.2224000000'.............auf 3 Stelle(n): '0.222'.................... OK
'0.2225000000'.............auf 3 Stelle(n): '0.223'.................... OK
'0.49999999'...............auf 0 Stelle(n): '0'........................ OK
'0.5'......................auf 0 Stelle(n): '1'........................ OK
'.5670'....................auf 3 Stelle(n): '0.567'.................... OK
'.5674'....................auf 3 Stelle(n): '0.567'.................... OK
'.5678'....................auf 3 Stelle(n): '0.568'.................... OK
'0.999999999'..............auf 0 Stelle(n): '1'........................ OK
'1'........................auf 2 Stelle(n): '1'........................ OK
'1.295'....................auf 2 Stelle(n): '1.3'...................... OK
'1.455'....................auf 2 Stelle(n): '1.46'..................... OK
'1.9'......................auf 0 Stelle(n): '2'........................ OK
'8.9964'...................auf 1 Stelle(n): '9'........................ OK
'8.9964'...................auf 0 Stelle(n): '9'........................ OK
'8.9964'...................auf 2 Stelle(n): '9'........................ OK
'9.999'....................auf 3 Stelle(n): '9.999'.................... OK
'9.999'....................auf 2 Stelle(n): '10'....................... OK
'9.999'....................auf 1 Stelle(n): '10'....................... OK
'9.999'....................auf 0 Stelle(n): '10'....................... OK
'12.2345678905'............auf 9 Stelle(n): '12.234567891'............. OK
'00000034.999'.............auf 2 Stelle(n): '35'....................... OK
'57'.......................auf 2 Stelle(n): '57'....................... OK
'456.4'....................auf 0 Stelle(n): '456'...................... OK
'456.5'....................auf 0 Stelle(n): '457'...................... OK
'12345.34'.................auf 1 Stelle(n): '12345.3'.................. OK
'12345.35'.................auf 1 Stelle(n): '12345.4'.................. OK
'2132343.565'..............auf 2 Stelle(n): '2132343.57'............... OK
'999999999999.994'.........auf 2 Stelle(n): '999999999999.99'.......... OK
'999999999999.999'.........auf 2 Stelle(n): '1000000000000'............ OK
'1e+100'...................auf 3 Stelle(n): '1e+100'................... OK
(7.56*1.19)................auf 2 Stellen : '9'........................ OK
(-7.56*1.19)...............auf 2 Stellen : '-9'....................... OK
(.57*100)..................auf 0 Stellen : '57'....................... OK
(.57*100)..................auf 2 Stellen : '57'....................... OK
'0.0000000075'.............auf 9 Stelle(n): '7e-009'................... FEHLER! Erwartet '0.000000008'
2009-12-12T09:53:58 topegWenn so ein Problem bei dir auftaucht, dann rundet deine Funktion manchmal falsch.
perl -e '$i=0; $ii=0.05; for(0..30){ $i+=0.05; $ii+=0.1 if($_%2); } print "$i-$ii=".($i-$ii)."\n"'
1.55-1.55=0
1.55-1.55=4.44089209850063e-16
2009-12-12T15:23:23 topegEntschuldige, verstehe ich nicht. Warum willst du dann überhaupt runden, wenn du eine Liste aller Zahlen hast, die du verwenden willst, und die dazu gehörige korrekte Rundung. Speiche die Liste und hole dir den richtigen Wert, ist noch um einiges schneller als das rechnen.
2009-12-12T15:23:23 topegAber wenn du nur einen Wert hast den du vorher nicht getestet hast, kannst du dir nicht sicher sein, ob dieser Wert auch korrekt gerundet wird.
2009-12-12T15:23:23 topegUnd wie ich schon sagte, wenn du vorher mit den Werten gerechnet hast, dann können sich Versteckte Ungenauigkeiten ansammeln, die sich beim Runden bemerkbar machen.
QuoteIch sehe keine kaufmännische Anwendung, die den von Dir beschriebenen theoretischen Fall hervorrufen könnte.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
4.015 auf 2 Stelle(n): bianca:4.01 <> topeg:4.02
4.145 auf 2 Stelle(n): bianca:4.14 <> topeg:4.15
4.185 auf 2 Stelle(n): bianca:4.18 <> topeg:4.19
4.225 auf 2 Stelle(n): bianca:4.22 <> topeg:4.23
4.265 auf 2 Stelle(n): bianca:4.26 <> topeg:4.27
4.395 auf 2 Stelle(n): bianca:4.39 <> topeg:4.4
4.435 auf 2 Stelle(n): bianca:4.43 <> topeg:4.44
4.475 auf 2 Stelle(n): bianca:4.47 <> topeg:4.48
4.515 auf 2 Stelle(n): bianca:4.51 <> topeg:4.52
4.645 auf 2 Stelle(n): bianca:4.64 <> topeg:4.65
4.685 auf 2 Stelle(n): bianca:4.68 <> topeg:4.69
4.725 auf 2 Stelle(n): bianca:4.72 <> topeg:4.73
4.765 auf 2 Stelle(n): bianca:4.76 <> topeg:4.77
4.895 auf 2 Stelle(n): bianca:4.89 <> topeg:4.9
4.935 auf 2 Stelle(n): bianca:4.93 <> topeg:4.94
4.975 auf 2 Stelle(n): bianca:4.97 <> topeg:4.98
2009-12-12T21:34:54 topegCode: (dl )1
24.015 auf 2 Stelle(n): bianca:4.01 <> topeg:4.02
4.145 auf 2 Stelle(n): bianca:4.14 <> topeg:4.15
Aber auch Egal, hast dich ja schon entschieden.
2009-12-13T08:11:16 biancaAlso kommt pq's Ansatz doch wieder in den Papierkorb, schade, ein Einzeiler wäre schon Klasse gewesen.
2009-12-13T13:15:38 pqfehler, die durch aufsummieren entstehen, kann man nicht korrekt verlässlich runden.
1 2
use Math::Round qw/ nearest /; my $rounded = nearest(1, $num * (10 ** $stellen)) / (10 ** $stellen);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1.00185 auf 4 Stelle(n): pq:1.0018 <> topeg:1.0019
1.00195 auf 4 Stelle(n): pq:1.0019 <> topeg:1.002
1.00395 auf 4 Stelle(n): pq:1.0039 <> topeg:1.004
1.00595 auf 4 Stelle(n): pq:1.0059 <> topeg:1.006
1.00795 auf 4 Stelle(n): pq:1.0079 <> topeg:1.008
1.00805 auf 4 Stelle(n): pq:1.008 <> topeg:1.0081
1.00995 auf 4 Stelle(n): pq:1.0099 <> topeg:1.01
1.01005 auf 4 Stelle(n): pq:1.01 <> topeg:1.0101
1.01195 auf 4 Stelle(n): pq:1.0119 <> topeg:1.012
1.01205 auf 4 Stelle(n): pq:1.012 <> topeg:1.0121
1.01395 auf 4 Stelle(n): pq:1.0139 <> topeg:1.014
1.01405 auf 4 Stelle(n): pq:1.014 <> topeg:1.0141
1.01595 auf 4 Stelle(n): pq:1.0159 <> topeg:1.016
1.01605 auf 4 Stelle(n): pq:1.016 <> topeg:1.0161
1.01805 auf 4 Stelle(n): pq:1.018 <> topeg:1.0181
1.02005 auf 4 Stelle(n): pq:1.02 <> topeg:1.0201
1.02015 auf 4 Stelle(n): pq:1.0201 <> topeg:1.0202
1.02205 auf 4 Stelle(n): pq:1.022 <> topeg:1.0221
1.02215 auf 4 Stelle(n): pq:1.0221 <> topeg:1.0222
1.02405 auf 4 Stelle(n): pq:1.024 <> topeg:1.0241
2009-12-13T13:53:59 topegZwar besser aber nicht perfekt:
Code: (dl )1.00995 auf 4 Stelle(n): pq:1.0099 <> topeg:1.01
'1.00995'..................auf 4 Stelle(n): '1.001'.................... FEHLER! Erwartet '1.01'
2009-12-13T13:42:00 pqok, für diesen testfall (der war ja in deiner ursprünglichen liste nicht mit drin) biete ich noch folgenden code an:
Code (perl): (dl )1 2use Math::Round qw/ nearest /; my $rounded = nearest(1, $num * (10 ** $stellen)) / (10 ** $stellen);
1
2
3
4
'-0.05499999999999999334'..auf 2 Stelle(n): '-0.06'.................... FEHLER! Erwartet '-0.05'
'-0.0000000075'............auf 9 Stelle(n): '-8e-009'.................. FEHLER! Erwartet '-0.000000008'
'0.0000000075'.............auf 9 Stelle(n): '8e-009'................... FEHLER! Erwartet '0.000000008'
'0.05499999999999999334'...auf 2 Stelle(n): '0.06'..................... FEHLER! Erwartet '0.05'
2009-12-13T14:19:14 pqbei der ersten und letzten zahl frage ich mich, wie du im realen kaufmännischen leben auf solche krummen ausgangszahlen kommst. bei den beiden mittleren frage ich mich, warum du es als fehler zählst, wenn 8e-009 rauskommt und 0.000000008 erwartet wird. das printf wirst du wohl schon noch selber hinbekommen, oder? 8e-009 ist gleich 0.000000008.
2009-12-12T00:40:51 topegEDIT: Kleinen Fehler beseitigt der das erkennen bestimmter Zahlen in meiner Rundungs-Funktion verhinderte.
1
2
'-0.0000000075'............auf 9 Stelle(n): '-8e-009'.................. FEHLER! Erwartet '-0.000000008'
'0.0000000075'.............auf 9 Stelle(n): '8e-009'................... FEHLER! Erwartet '0.000000008'
2009-12-13T09:14:47 topegAlso bei mir funktioniert das. Wie erzeugst du die Zahlen und wie vergleichst du sie?
1 2 3 4 5 6 7 8 9 10 11
my %test = ( '0.0000000075,9' => '0.000000008', '-0.0000000075,9' => '-0.000000008', ); my $space = '.' x 25; foreach my $test (sort {(split /,/,$a)[0] <=> (split /,/,$b)[0] } keys %test) { my ($wert,$stellen) = split /,/,$test; my $gerundet = &{$sub} ($wert,$stellen); print "\'$wert\'" . substr ($space,0,25 - length ($wert)) . "auf $stellen Stelle(n): \'$gerundet\'" . substr ($space,0,25 - length ($gerundet)) . ($gerundet eq $test{$test} ? ' OK' : " FEHLER! Erwartet \'$test{$test}\'") . "\n"; }
2009-12-13T10:37:37 topegBei Zahlen mit "eq"? Besser doch "=="
1
2
'4.685'....................auf 2 Stelle(n): '4.69'..................... FEHLER! Erwartet '4.69'
'4.935'....................auf 2 Stelle(n): '4.94'..................... FEHLER! Erwartet '4.94'
"8e-9"
"0.000000008"
1
2
3
4
5
6
1.505162-1505162e-6=-2.22044604925031e-16
1.510463-1510463e-6=2.22044604925031e-16
1.520787-1520787e-6=-2.22044604925031e-16
1.526088-1526088e-6=2.22044604925031e-16
1.536412-1536412e-6=-2.22044604925031e-16
1.541713-1541713e-6=2.22044604925031e-16
2009-12-13T13:18:56 topegIch habe da eine auskommetierte "sprintf"-Zeile, die sollte das Problem beheben, da es die "kaputten" Stellen abschneidet. (dann kann man sich auch das "+0" beim "return" sparen.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
my $wert = shift (@_) || 0; my $stellen = shift (@_) || 0; my $vorzeichen = ''; my $vorkomma = 0; my $nachkomma = ''; if ($stellen >= 0 && $stellen <= 9 && !($wert =~ /[^+-\.0-9]/)) { if (substr ($wert,0,1) eq '-' || substr ($wert,0,1) eq '+') { $vorzeichen = '-' if substr ($wert,0,1) eq '-'; $wert = substr ($wert,1); } ($vorkomma,$nachkomma) = split /\./,$wert; $vorkomma = 0 unless $vorkomma; $nachkomma = '' unless $nachkomma; if (length ($nachkomma) > $stellen) { my $auf = (substr ($nachkomma,$stellen,1) > 4 ? 1 : 0); if (!$stellen) { $vorkomma ++ if $auf; $nachkomma = ''; } else { if ($auf) { if (substr ($nachkomma,0,$stellen) eq '9' x $stellen) { $vorkomma ++; $nachkomma = ''; } else { my ($count) = $nachkomma =~ m!^(0*)!; my $vornull = length $count; $vornull -- if substr ($nachkomma,0,$stellen) eq '0' x $stellen; my $temp = substr ($nachkomma,0,$stellen) + 1; $nachkomma = ($vornull ? '0' x $vornull : '') . $temp; } } else { $nachkomma = substr ($nachkomma,0,$stellen); } } } } $vorkomma =~ s/^0+([^0])/$1/; $nachkomma =~ s/0+$//; return ("$vorkomma.$nachkomma" / 1 ? $vorzeichen : '') . "$vorkomma" . ($nachkomma ne '' && substr ($nachkomma,0,$stellen) ne '0' x $stellen ? ".$nachkomma" : '');
1 2 3 4 5 6 7 8 9 10 11 12 13
# reconstruct number $zahl="$pre${number}e$exponent"; # format number if($zahl==0) { $zahl='0'; } else { $zahl=sprintf("%.${stelle}f",$zahl); $zahl=~s/\.?0+$// if(index($zahl,'.')>0) } return $zahl;
2009-12-13T13:41:54 topegFormatierungscode:
1
2
3
4
5
6
7
8
9
0.01234 auf 2 stellen runden:
# umwandeln
-> 0.01234 = 1234 * 10**-5
# letzten drei stellen "-(-5+2)" nehmen und aufaddieren:
-> 1234 + 123=1468
# die letzten drei Stellen weg nehmen:
-> 1000
# zurück wandeln
-> 1000* 10**-5=0.01
0.0005e-5
2009-12-13T14:28:30 topegDas Problem umgehe ich indem ich die Zahl als "$integer * 10**$exponent" betrachte.
2009-12-13T14:38:20 biancaDie von MatthiasW ist auch schick, braucht aber ein Modul, das ist mir bei so einer zentralen Funktion nicht ganz so recht.
2009-12-13T14:46:20 ?edit: interessant ist auch, dass du ja irgendwann in dieser diskussion selbst den vorschlag gemacht hast, aus der funktion ein modul zu bauen. jetzt ist eins im core, und dein grund dagegen ist, dass es ein modul ist...
1 2 3 4 5 6 7 8 9
sub kfmrunden_bignum { my( $num, $pos ) = @_; my $bignum = Math::BigFloat->new( $num ); # $pos = ( $bignum->length )[1] unless defined $pos; # huch, was hab ich mir denn dabei gedacht?! $pos ||= 0; return 0 + $bignum->ffround( -$pos, 'common' )->bstr; } # kfmrunden_bignum
2009-12-13T14:19:39 MatthiasWUm auch mal eine Lösung mittels Math::BigFloat
1 2 3 4 5 6 7 8
# format number if($zahl==0) { $zahl='0'; } else { $zahl=sprintf("%.${pos}f",$zahl); $zahl=~s/\.?0+$// if(index($zahl,'.')>0) }
2009-12-13T14:24:58 biancaEdit: Frage: Gehört das Modul zum Perl Core?
2009-12-13T14:28:28 pqwie wärs, wenn du sie dir bookmarken würdest?
2009-12-13T15:46:20 topegUnd trotzdem ist meines 6-7 mal schneller! ;-)
2009-12-13T15:16:48 pqmal ne ganz andere frage: warum verwendest du nicht Test::More für sowas? die funktion cmp_ok() nimmt dir den ganzen kram mit "ergebnis vs. erwartet" ab. (und im core ist das modul auch schon recht lange. sollte man eh kennen, wenn man tests schreibt.)