![]() |
Nur x Nachkommastellen von Float
Nabend zusammen,
es gibt ja System.Trunc, um den Integer Teil eines Floats zu bekommen. Gibt es quasi als Gegenstück eine Funktion die nur die ersten Ziffern des Dezimalteils liefert? Also z.B. sowas
Code:
Meine Alternative funktioniert auch aber finde ich nicht schön:
GetDigits(10.234567, 3); => "234"
|
AW: Nur x Nachkommastellen von Float
Punkt 1 ließe sich auch durch
Delphi-Quellcode:
ersetzen.
Frac(x)
Wenn es Runden statt Abschneiden sein darf, dann wäre
Delphi-Quellcode:
eine Alternative. Theoretisch kann man vorher mit
RoundTo
Delphi-Quellcode:
einstellen, wie das erfolgen soll (damit ginge auch ein truncate), aber das ist natürlich nicht threadsicher.
SetRoundMode
|
AW: Nur x Nachkommastellen von Float
hallo,
evtl. so (Rohversion)
Delphi-Quellcode:
Gruß
function GetDigits(Value:EXTENDED;digit:integer):Extended;
var value1, value2:extended; begin value1:=0; value2:=0; value1:=frac(value); value2:=Power(10,digit); result:=trunc(value1*value2); end; |
AW: Nur x Nachkommastellen von Float
Da digit ein Integer ist, bietet sich auch
![]()
Delphi-Quellcode:
function GetDigits(const AValue: Double; Digits: Integer): Integer;
begin Result := Trunc(Frac(AValue)*IntPower(10, Digits)); end; |
AW: Nur x Nachkommastellen von Float
Vielen Dank, das sieht schon eleganter/lesbarer aus!
|
AW: Nur x Nachkommastellen von Float
![]() Beispiel 0.1(10) ist binär geschrieben periodisch 0.000110011001100....(2) mit Periode 1100. D.h. die Dezimalzahl 0.1 lässt sich als double nicht genau abspeichern. Es werden nur die ersten 52 Bits NACH der ersten 1 (implizites Bit) abgespeichert. Wenn zum Beispiel genau die ersten 52 Bits der Binärdarstellung eines solchen Bruchs abgespeichert werden, dann ist der effektiv abgespeicherte Wert etwas zu klein (Bits 53+ gehen alle verloren).
Delphi-Quellcode:
wobei GetDigits die weiter oben vorgeschlagene Funktion ist.
procedure testD;
var avalue, d1, d2 : double; a,b, res : integer; begin avalue := 98.103; d1 := frac(avalue); d2 := d1 * 1000; res := trunc(d2); showmessage(res.ToString); showmessage(getdigits(avalue,3).ToString ); end; Wenn der Code oben in Delphi ausgeführt wird, dann ist d1=0.102999999999994; der Output lautet beide Male 102; wir hätten gern 103. Was helfen kann: Wenn man den gespeicherten Wert um den kleinstmöglichen Wert Epsilon = 0 00 00 00 00 00 01 (hex) erhöht, dann liegt im Speicher neu eine etwas zu grosse Zahl. trunc funktioniert dann wie erwartet. Hinweis: Der Code plusepsilon (unten) kann Plattform abhängig sein und berücksichtigt den Fall "alle 52 Mantissenbits=1" nicht.
Delphi-Quellcode:
(Es gibt natürlich weitere, weniger "mathematische" Lösungen... ;-))
function plusepsilon(d: Double): Double;
// addiert die kleinstmögliche zahl zu d. // Bedingung: d>0 var Bits: Int64 absolute d; begin Inc(Bits); Result := d; end; procedure testD; var avalue, d1, d2 : double; a,b, res : integer; begin avalue := plusepsilon(98.103); .... |
AW: Nur x Nachkommastellen von Float
Hallo Michael,
so einfach läßt sich das Problem der Rundungsfehler leider nicht lösen. Eigentlich gibt es überhaupt keine Lösung dafür. Denn es wird nicht nur abgerundet, sondern auch mal aufgerundet. Und dann müßte Dein plusepsilon ein minusepsilon sein. Sorry, Deine Lösung ist nicht „mathematisch“, sondern willkürlich an diese eine Aufgabe zugeschnitten. Was einzig alleine hilft, sind mehr, vieeeeeel mehr Stellen, also nicht Double, sondern Extended, quad precision, oder oft nur Multi-Precisions-Arithmetik (MPA) mit hunderten ... tausenden Nachkommastellen, wobei das Ergebnis am Ende aller Berechnungen auf das Zielformat gerundet wird. Über diese Problematik haben sich schon viele Forscher den Kopf zerbrochen und zahlreiche gute Artikel und Fachbücher publiziert, ohne ein Patentrezept verkünden zu können... |
AW: Nur x Nachkommastellen von Float
Zitat:
Wenn ein Dezimalbruch binär periodisch ist, dann wird das System beim Speichern von double wahrscheinlich entweder aufrunden, wenn das 53ste Bit=1 ist oder aber ab Bit 53 abschneiden (und damit abrunden). Das System speichert also entweder z1<z oder z2>z. Im Intervall ]z1,z2[ kann das Format nix speichern. Es gibt also 2 Fälle. 1. Das System hat abgeschnitten oder abgerundet und speichert z1. Wir addieren Epsilon und landen bei z2>z und trunc() wird den erwarteten Wert ermitteln. 2. Das System hat aufgerundet und hat z2 gespeichert. Wir könnten also mit z2 weiterrechnen und trunc() wäre OK. z3:=plusepsilon(z2)=z2+Epsilon ist der nächstgrössere speicherbare Wert nach z2. Also z3>z2>z. Wenn die Stellengenauigkeit vom verwendeten Format gross genug ist für das gegebene Problem, dann fällt es nicht ins Gewicht, dass wir nicht mit z2 weiterrechnen. Wenn wir aber wie du schreibst abrunden würden, dann würden wir bei z1 := z2-Epsilon landen. Wir wissen aber z1<z und damit rechnen "wir" beim trunc() wieder falsch. Also keine gute Idee. |
AW: Nur x Nachkommastellen von Float
Zitat:
Mit Willkür hat das nix zu tun. Aber du hast Recht, das ist 1. Semster Informatik Stoff und hat mit Mathe nicht viel zu tun - eher mit Rechnen. Du schreibst ![]() Dezimalbrüche, welche binär periodisch sind können in einem binären Format (wie von single, double, extended verwendet) nie genau gespeichert werden. Mit extended erhöhst du lediglich die Stellengenauigkeit (extended: floor(log(2^63)/log(10) - generell log(2^n)/log(2) = n*log(2)/log(10) - d.h. pro weiteres Bit für den fraction Teil gewinnst du ungefähr 0.301 Stellen). Wenn du für das in #1 geschilderte Problem als Input Zahlen mit vielen Stellen erwartest, dann nimm extended. Für die trunc()erei wirst du bei solchen Datentypen um das Epsilon nicht herumkommen. Es gibt sicher Leute, welche das Speichern und das Rechnen mit solchen Daten als "Forschung" bezeichnen. Mich wundert heute nix mehr ;-) |
AW: Nur x Nachkommastellen von Float
Hallo,
als Korrektur zu #3, (mit DOUBLE gab's Probleme)
Delphi-Quellcode:
function GetDigits(const AValue: Extended; Digits: Integer): Integer;
var value:Extended; value1:Real; begin value:=frac(AValue)*intpower(10,digits); value1:=value; Result := trunc(value1); end; Gruß |
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:22 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz