Bibliotheken: Math Utils - Seite 5

 
FXAI #:
Dies sind drei praktische Funktionen für den Vergleich und die Rundung von Fließkommazahlen und die Formatierung von Geld:

1. `bool DoubleEquals(double x, double y, double eps)` vergleicht zwei Double-Werte `x` und `y` mit einem gegebenen Epsilon-Wert `eps` und gibt einen boolschen Wert zurück, der angibt, ob sie innerhalb der gegebenen Toleranz gleich sind.

2. `double RoundTo(double value, int digits)` rundet einen double-Wert `value` auf die angegebene Anzahl von Dezimalstellen `digits`.

3. `string FormatMoney(double amount)` formatiert einen Double-Wert `amount` als String, der einen Währungsbetrag darstellt. Es formatiert den Betrag mit zwei Dezimalstellen, ersetzt den Dezimalpunkt durch ein Komma und fügt alle drei Stellen Leerzeichen ein, um die Lesbarkeit zu verbessern. Außerdem wird am Ende das Währungssymbol aus `AccountInfoString(ACCOUNT_CURRENCY)` angefügt.

Vielen Dank dafür. Allerdings sind diese Funktionen bereits in der Bibliothek implementiert (sogar mit stabileren Ergebnissen als die Ihren), aber mit anderen Namen.

// Prüfen, ob zwei Zahlen bis auf "n" Stellen genau gleich sind.
int    Compare(const double a, const double b, const int digits);
bool   EqualDoubles(double a, double b, int significantDigits = 15);
bool   IsClose(const double a, const double b, const int maxDifferentSignificantDigits = 2)

// Genaue Dezimalrundung, um unerwartete Ergebnisse zu vermeiden.
double Round(const double v);                       
double Round(const double value, const int digits); 
double Round(const double value, const double step);

// Formatiert Double mit Tausendertrennzeichen und angegebenen Dezimalstellen.
string FormatDouble(const double number, const int digits, const string separator=",");

 

Hallo @amrali, danke für deinen Beitrag.

Vielleicht ist das ein Fehler?

Ich hatte erwartet, dass der zweite Druck "0.0001" sein sollte.

Wenn es ein Fehler ist, wie kann man ihn beheben? Wenn nicht, was ist in meinem Code falsch?

Ich danke Ihnen.

double ask = 1.2973;
double bid = 1.2972;
double spread = ask - bid;

Print(spread);// Ausgaben: 0.00009999999999998899
Print(StripError(spread));// Ausgaben: 0.000099999999999989


amrali
amrali
  • 2024.04.05
  • www.mql5.com
Trader's profile
 
jonlinper #:

Hallo @amrali, vielen Dank für Ihren Beitrag.

Vielleicht ist dies ein Fehler?

Ich hatte erwartet, dass der zweite Druck "0.0001" sein sollte.

Wenn es ein Fehler ist, wie kann man ihn beheben? Wenn nicht, was ist in meinem Code falsch?

Vielen Dank!


Drucken Sie die hexadezimalen Darstellungen aus und Sie werden verstehen, dass die Spanne weit vom wahren realen Wert 0,0001 entfernt ist (das liegt an Rundungsfehlern bei der Subtraktion).

Sie müssen also Rundungsprozeduren anwenden.

   double ask = 1.2973;
   double bid = 1.2972;
   double spread = ask - bid;

   Print(spread);                                  // Ausgaben: 0.00009999999999998899
   Print(StripError(spread));                      // Ausgaben: 0.000099999999999989

   Print(DoubleToHexadecimal(spread));             // Ausgänge: 0x3F1A36E2EB1C4000
   Print(DoubleToHexadecimal(StripError(spread))); // Ausgänge: 0x3F1A36E2EB1C4001
   Print(DoubleToHexadecimal(0.0001));             // Ausgänge: 0x3F1A36E2EB1C432D

   Print(EQ(spread, 0.0001));                      // Ausgaben: true
   Print(Round(spread, 16));                       // Ausgänge: 0.001

Es gibt kleine Unterschiede, die Sie beachten sollten:

StripError() rundet an der 16 . Stelle 0.00009999999999998899 (0's werden nicht gezählt).

Round(x, 16) rundet auf die 16. Stelle nach dem Komma 0.00009999999999998899

 
jonlinper #: Ich hatte erwartet, dass der zweite Ausdruck "0,0001" lautet.

Fließkommazahlen haben eine unendliche Anzahl von Dezimalstellen. Es liegt an dir, dass du das Fließkommaformat nicht verstehst und dass einige Zahlen nicht exakt dargestellt werden können. (wie 1/10.)
Doppeltes Gleitkommaformat - Wikipedia

Siehe auch Der == Operand. - MQL4 Programmierforum (2013)

Wenn Sie die korrekte Anzahl von Ziffern sehen wollen, konvertieren Sie sie in eine Zeichenkette mit der richtigen/gewünschten Genauigkeit.
Frage zu decima von marketinfo() - MQL4 Programmierforum (2016)

 
William Roeder #:

Die Fließkommadarstellung hat eine unendliche Anzahl von Dezimalstellen. Es liegt an Ihnen, dass Sie das Fließkomma-Format nicht verstehen und dass einige Zahlen nicht exakt dargestellt werden können. (wie 1/10.)
Doppeltes Gleitkommaformat - Wikipedia

Siehe auch Der == Operand. - MQL4-Programmierforum (2013)

Wenn Sie die korrekte Anzahl von Ziffern sehen wollen, konvertieren Sie sie in eine Zeichenkette mit der richtigen/gewünschten Genauigkeit.
question about decima of marketinfo() - MQL4 programming forum (2016)

Lieber William, danke für die Klarstellung, allerdings stimme ich dir nicht zu, was die "unendliche Anzahl von Dezimalstellen" angeht. FP-Zahlen haben tatsächlich eine endliche Anzahl von Dezimalstellen. (Zum Beispiel hat 0,1 genau 52 Ziffern nach dem Komma).

Verwenden Sie DoubleToStringExact(0.1) aus meiner Bibliothek, um sie alle zu drucken. Sie können auch die vollständige Dezimalzeichenfolge mit diesem Rechner hier überprüfen: https://www.exploringbinary.com/floating-point-converter/
Beachten Sie auch, dass die vollständige Dezimalzeichenfolge immer mit der Ziffer "5" enden muss.

0.1000000000000000055511151231257827021181583404541015625
 

Wie kann man am besten nur signifikante Ziffern mit Doppelzahlen drucken?

double Trunc(const double value, const int digits);

Diese Funktion funktioniert gut für 99,9% der Zahlen, aber sie hat Probleme mit runden Zahlen wie 1.0000000000

Mein Problem ist, dass ich die nicht signifikanten Ziffern entfernen muss, und aus irgendeinem Grund bin ich nicht in der Lage, dies nur mit @Trunc zu tun,

also habe ich etwas verwendet wie:

string Normalize_Double_ToString(double n, int d)
{
   // Schritt 1 - es hilft, Nullen am Ende auszuschließen
   n = Round(n, d);

   // Schritt 2 - Zählen der Anzahl der signifikanten Dezimalstellen
   int sd = GetSignificantDecimals(n);

   // Schritt 3 - wir wollen nicht mehr als in @d angegeben
   if (sd > d){ sd = d; }

   // Schritt 4 - Entfernen unerwünschter Dezimalstellen ohne negative Rundung 
   double t = Trunc(n, sd);

   // Fehlersuche
   //PrintFormat("%s [%d] [%d] :: %s", DoubleToString(n, DBL_DIG), d, sd, DoubleToString(t, sd));

   // Schritt 5 - Genauigkeit einstellen
   string s = DoubleToString(t, sd);

   return s;
}

Es funktioniert genau wie benötigt und gibt mir die kleinste mögliche Zeichenkette für alle Zahlen, aber ich frage mich, ob es optimiert werden kann und immer noch die kleinste Zeichenkette mit runden Zahlen wie 1.00000000

Ich danke Ihnen für Ihre Hilfe.

 

Ich habe gerade festgestellt, dass ich die

int GetSignificantDecimals(double value)

Eine leicht modifizierte Version Ihrer

int GetSignificantDigits(double value)

und dies ist der Code

int GetSignificantDecimals(double value)
{
   if(!value || !MathIsValidNumber(value))
   {
      return 0;
   }

   // Summe der Dezimalstellen
   int digits = GetDigits(value);

   // ohne nachgestellte Nullen
   while(MathMod(value, 10) == 0)
   {
      digits--;
   }

   return digits;
}
 
Cristian Dan Fechete runden Zahlen wie 1.0000000000

Mein Problem ist, dass ich nicht signifikante Ziffern entfernen muss, und aus irgendeinem Grund bin ich nicht in der Lage, es nur mit @Trunc zu tun,

also benutze ich etwas wie:

Es funktioniert genau wie benötigt und gibt mir die kleinste mögliche Zeichenfolge für alle Zahlen, aber ich frage mich, ob es optimiert werden kann und immer noch kleinste Zeichenfolge mit runden Zahlen wie 1.00000000

Danke für die Hilfe!

Entschuldigung, Sie müssen verstehen, was signifikante Ziffern sind, da ich finde, dass Ihr Code die grundlegenden Konzepte verwirrt.
Was versuchen Sie zu tun? Bitte erklären Sie es in einfachen Worten, ohne Code. Geben Sie ein Beispiel dafür, was Sie falsch finden und was Ihre Erwartungen sind.
 
amrali #:
Entschuldigung, Sie müssen verstehen, was signifikante Ziffern sind, da ich finde, dass Ihr Code die grundlegenden Konzepte verwirrt.
Was versuchen Sie zu tun? Bitte erklären Sie es in einfachen Worten, ohne Code. Geben Sie ein Beispiel dafür, was Sie falsch finden und was Ihre Erwartungen sind.

Vielen Dank für Ihre Zeit, und ja, ich bin mir nicht ganz sicher, ob ich "signifikante Ziffern" verstehe.

Im Grunde genommen muss ich die kürzestmögliche Zahl "drucken". Zum Beispiel:

1.0000000 -> 1

1.0090000 -> 1.009

123.00100 -> 123.001

Für mich bedeutet 'signifikante Ziffern': Ziffern, die den Wert einer Zahl ändern, wenn sie entfernt werden, also sind nachgestellte Nullen nicht signifikant.


Übrigens, seit dem letzten Windows-Update führt die Funktion Round(double, int) dazu, dass MT4 blockiert. Der erste Code, den ich gepostet habe, funktionierte perfekt und seit gestern Abend friert der MT4-Client komplett ein.

 
Cristian Dan Fechete #:

Vielen Dank für Ihre Zeit, und ja, ich bin mir nicht ganz sicher, ob ich "signifikante Ziffern" verstehe.

Ich muss im Grunde die kürzestmögliche Zahl "drucken". Zum Beispiel:

1.0000000 -> 1

1.0090000 -> 1.009

123.00100 -> 123.001

Für mich bedeutet "signifikante Ziffern": Ziffern, die den Wert einer Zahl verändern, wenn sie entfernt werden, also sind nachgestellte Nullen nicht signifikant.


Übrigens, seit dem letzten Windows-Update führt die Funktion Round(double, int) dazu, dass MT4 blockiert. Der erste Code, den ich gepostet habe, funktionierte perfekt und seit gestern Abend friert der MT4-Client komplett ein.

Die Print()-Funktion oder die Umwandlung von double in string wie (string)dbl liefert die kürzest mögliche Anzahl signifikanter Ziffern, ohne dass man die Zahl vorher manipulieren muss. Dies ist eine eingebaute Funktion in MQL. (Ich habe dem Entwicklerteam bereits eine Lösung vorgeschlagen, und sie wurde in den Code aufgenommen).

Alles, was Sie tun müssen, ist:
string num_str = string(number).
oder: Print(number);

Aus diesem Grund ist eine spezielle Funktion zum Drucken oder Formatieren von Zahlen in die kürzest mögliche Zeichenkette innerhalb der Bibliothek nicht erforderlich, die Funktionalität wird bereits von der MQL-Sprache unterstützt.

Verwenden Sie DoubleToString() nur, wenn Sie die Anzahl der Ziffern nach dem Dezimalpunkt kontrollieren müssen. Wenn der Parameter digits größer ist als die Dezimalstellen in Ihrer Zahl, werden die 0's an den zurückgegebenen String angehängt, zum Beispiel
DoubleToString(1.09, 5) gibt die Zeichenkette "1.09000" zurück.

Ist der Parameter digits kleiner als die Dezimalstellen der Zahl, wird die Zahl angenähert, z. B. DoubleToString(1.12345, 2) gibt die Zeichenfolge "1.12" zurück. Die Verwirrung entsteht durch die Unfähigkeit, Zahlen von Zeichenketten zu unterscheiden.

Beachten Sie, dass Zahlen, die mit Nullen enden, wie 1.09, 1.090, 1.0900, 1.09000 und 1.090000 als dieselbe Double-precision fp Zahl in Variablen gespeichert werden. Diese können nur durch direkte Eingabe durch den Benutzer als manuelle Eingaben erreicht werden. Innerhalb des Programms werden alle diese Zahlen als dieselbe Zahl, nämlich 1,09, gespeichert, nachgestellte 0 werden nicht gespeichert.

double a = 1.09;
double b = 1.090000;
Print(a); // "1,09"
Print(b); // "1,09"

Rundungsfunktionen wie round, ceil und floor ändern (approximieren) die eingegebene Zahl in eine andere Zahl, die die nächstgelegene Double-Zahl mit der angegebenen Anzahl von Nachkommastellen ist, oder die Zahl, die die angegebene Gesamtzahl signifikanter Ziffern enthält, im Falle von RoundToSignificantDigits().

Ich hoffe, das klärt die Verwirrung über die Konvertierung von doppelgenauen fp-Zahlen in einen String.