User since
2003-08-04
7321
Artikel
ModeratorIn
Moin, moin
hab da mal ein Problem, zu dem ich schon eine unschöne (und wohl umständliche Lösung gefunden hab)
Und zwar hab ich folgendes:
ich habe decimalzahlen in hoher genauigkeit.
diese werden gerundet.
Beispiel:
35,2374528376 => 35,24
542,47123 => 542,47
soweit so gut.
Im obigen Fall ist meine Einheit=0,01 (keine ahnung, ob dass nun wirklich einheit heißen darf, aber ich nenn das mal so)
wäre die einheut 0,001 gewesen, hätte obiges beispiel so ausgesehen
35,2374528376 => 35,237
542,47123 => 542,471
so; leider kann es auch einheiten von 0,5; 0,05; ... geben
das sähe dann so aus
0,5:
35,2374528376 => 35,0
542,47123 => 542,5
0,05:
35,2374528376 => 35,20
542,47123 => 542,45
ich brauche jetzt also eine funktion, die aus einer zahl n und einer einheit u, die auf die einheit gerundete zahl x liefert.
freu mich schon auf die lösungen! ;)
PS: Fragen?
User since
2003-08-04
7321
Artikel
ModeratorIn
hier mal mein c# implementierung
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
public static decimal ToUnit(decimal number, decimal unit, MidpointRounding mode)
{
if (unit > 1m) unit = 1m;
string unitStr = unit.ToString();
int decimals = 0;
string separator = ThreadHelper.CurrentNumberFormat.NumberDecimalSeparator;
int idx = unitStr.IndexOf(separator);
if (idx >= 0)
{
bool nonZero = false;
unitStr = unitStr.Substring(idx + separator.Length);
foreach (char c in unitStr)
{
if (c == '0' && nonZero) break;
if (c != '0') nonZero = true;
decimals++;
}
}
number = decimal.Round(number, decimals, mode);
decimal i = decimal.ToInt32(number);
decimal f = (decimal)Math.Pow(10d, decimals);
decimal d = (number - i) * f;
decimal u = unit * f;
number = i + (d - d % u) / f;
return number;
}
kommt mir aber nur auf den algorithmus und nicht die sprache an!
User since
2006-11-29
340
Artikel
BenutzerIn
Da hast du dir wirklich zu viel Arbeit gemacht, so gehts auch:
n_r = u * sprintf('%.0f', n / u)
Oder als kleine Perlfunktion:
sub tounit($$) {
# pick parameters
my($num, $unit) = @_;
# check parameters
return undef unless looks_like_number($num);
return undef unless looks_like_number($unit);
return $unit unless $unit; # edit
# round number
my $res = $unit * sprintf( '%.0f', $num / $unit );
# return rounded number
return $res;
} # tounit
(check parameters funktioniert nur, wenn looks_like_number() von Scalar::Util mit eingebunden ist)
MfG\n\n
<!--EDIT|PerlProfi|1181827823-->
User since
2007-05-11
923
Artikel
HausmeisterIn
Ich würde der Rundungs-Funktion von sprintf misstrauen (hab in C damit schlechte Erfahrungen gemacht), und es so machen:
use POSIX qw(floor);
my $unit = 0.05;
my $number = 12.34567;
my $rounded = $unit * floor(0.5 + $number / $unit);
Das +0.5 garantiert "richtiges" runden, d.h. 0.5 wird zu 1 gerundet, 0.49 zu 0.
Damit geht man auch keinen Weg über einen String.
User since
2003-08-04
7321
Artikel
ModeratorIn
:blush:
danke.
manchmal sieht man den walt wohl vor vielen bäumen nicht!
User since
2003-08-04
7321
Artikel
ModeratorIn
bei einer unit von 0.5 versaut die +0.5 das ergebnis natürlich
User since
2003-08-04
7321
Artikel
ModeratorIn
in c# sieht es jetzt so aus
public static decimal ToUnit(decimal number, decimal unit, MidpointRounding mode)
{
if (unit > 1m) unit = 1m;
return unit * decimal.Round(number / unit, mode);
}
User since
2006-11-29
340
Artikel
BenutzerIn
Achja, u == 0 sollte man als Sonderfall behandeln, also einen speziellen Rückgabewert liefern ( evtl. auch 0 ) oder eine exception auslösen, oder ähnliches.
User since
2003-08-04
7321
Artikel
ModeratorIn
in C# wird das einfach zu NaN
User since
2003-08-04
7321
Artikel
ModeratorIn
ich frage mich gerade, inwieweit das
hiermit kollidiert