Wie wird in C# gerundet?

Das umwandeln von Float, Double oder Decimal in einen Integer funktioniert in C# nach der IEEE 754 Definition und ist somit nicht immer wie man es erwarten würde, wenn man nur den kaufmännischen Weg kennt. Zunächst einmal sollte erwähnt werden, dass es verschiedene Wege gibt aus einer Gleitkommazahl eine Ganzzahl zu machen. Ich werde euch hier die einzelnen Wege einmal vorstellen.

Der erste Weg, ist das direkte Umwandeln durch einen Datentyp-Cast. Hierbei werden jedoch Nachkommastellen einfach abgeschnitten statt wirklich gerundet zu werden. Dies würde wie folgt aussehen:

float myFloat = 4.8f;
int myInteger = (int)myFloat; // myInteger wird nun 4 sein.

Die nächste Möglichkeit ist das Umwandeln von Typen über die Convert-Klasse aus dem .NET-Framework.

float myFloat = 4.8f;
int myInteger = Convert.ToInt32(myFloat); // myInteger wird nun 5 sein.

Hierbei würden wir augenscheinlich zunächst das richtige Ergebnis erhalten, aber was ist, wenn unsere Zahl genau in der Mitte liegt? Hier kommt es dann zum Problem.

int myInteger1 = Convert.ToInt32(8.5); // myInteger1 wird nun 8 sein.
int myInteger2 = Convert.ToInt32(9.5); // myInteger2 wird nun 10 sein.

Um das Problem kurz aufzulösen: Hierbei wird immer zur nächsten geraden (sprich durch zwei teilbaren) Zahl gerundet. Im Falle von 8,5 also zur 8 und im Falle von 9,5 zur 10. Leider kommt es zu dem gleichen Problem, wenn man die Methode Math.Round verwendet. Es sei den, man benutzt eine Überladung für die Math.Round Methode unter der Angabe des „MidpointRounding„-Parameters. Mehr Informationen dazu gibt es hier:
https://msdn.microsoft.com/en-us/library/system.midpointrounding.aspx

Wusstet ihr schon… Enumerationen?

Enumerationen oder auch Aufzählungen sind für den Computer nicht mehr und nicht weniger als Integer-Typen. Jede Aufzählung (außer jene, bei welchen man dies verändert hat) lässt sich zum Integer wandeln und jeder Integer kann zu einer Aufzählung gewandelt werden (für letzteres gelten allerdings einige Einschränkungen, da die  Zahl in der Aufzählung präsent sein muss). Darum kann auch jedem Eintrag in einer Aufzählung eine Ganzzahl zugewiesen werden (in der Regel verwendet man hier die 2er-Potenzen um so mittels „Flag“-Attribut „oder“, „und“ „exklusiv oder“ und die anderen Bit-Operatoren verwenden kann).  Weiterlesen

Ein eigener Datentyp (Teil 6)

Im letzten Teil (siehe: Ein eigener Datentyp (Teil 5)) haben wir uns um die Konvertierung aus Standard-Datentypen gekümmert. In diesem Teil soll es nun um die überschriebenen Methoden der Basisklasse – Object – gehen. Hier müssen wir die Implementierung für drei Methoden vornehmen, „Equals(object)“, „GetHashCode()“ sowie „ToString()“. Beginnen wir mit der „Equals“-Methode. Diese ist eigentlich auch schon die „komplizierteste“ der drei genannten. Hier würde ich die folgende Implementierung vorschlagen:

public override bool Equals(object obj)
{
    return ((obj is Int24) && (Value == ((Int24)obj).Value)) || ((obj is int) && (Value == (int)obj));
}

Weiterlesen

Ein eigener Datentyp (Teil 5)

Im letzten Teil (siehe: Ein eigener Datentyp (Teil 4)) haben wir die „Parsing“-Methoden implementiert. In diesem Teil wird es um die Methoden zur Konvertierung aus Standardtypen gehen. Unter Standardtypen verstehen sich folgende: byte, sbyte,  short, ushort, int, uint, long, ulong, float, double, decimal sowie string (welchen wir theoretisch bereits im letzten Teil implementiert haben). Beginnen wir also mit den beiden Byte-Typen.

public static Int24 ConvertFromByte(byte value)
{
    return new Int24(value);
}

public static Int24 ConvertFromSByte(sbyte value)
{
    return new Int24(value);
}

Nicht weiter kompliziert. Da ein Byte niemals außerhalb der Reichweite unseres Typen sein kann, benötigen wir keine weiteren Aktionen. Dies wird bei späteren Typen anders aussehen und auch beim UInt24 würde dies unter Umständen wegen den vorzeichenbehafteten Typen anders aussehen. Weiterlesen

Ein eigener Datentyp (Teil 4)

Im letzten Teil (siehe: Ein eigener Datentyp (Teil 3)) haben wir die letzten Operatoren implementiert. In diesem Part geht es um die „Parsing“-Methoden. Diese Methoden sollen dazu dienen das Parsing von Strings zu ermöglichen. Hierfür gibt es an den Standarddatentypen die Statischen-Methoden „TryParse“ sowie „Parse“. Auch wir wollen nun diese Methoden implementieren und zur Verfügung stellen. Hierbei ist „TryParse“ quasi Exception sicher (hier werden die Exceptions gecatcht und anschließend ein false zurück gegeben). Beginnen wir mit den beiden Varianten der „TryParse“-Methode. Diese könnten wie folgt aussehen:

public static bool TryParse(string s, out Int24 result)
{
    return TryParse(s, NumberStyles.Integer, null, out result);
}

public static bool TryParse(string s, NumberStyles style, IFormatProvider formatProvider, out Int24 result)
{
    int output;
    bool canParse = Int32.TryParse(s, style, formatProvider, out output);

    if (!canParse || output > IntMaxValue || output < IntMinValue)
    {
        result = new Int24();
        return false;
    }

    result = new Int24(output);
    return true;
}

Während die kurze Fassung dieser Methode lediglich unsere längere aufruft, nutzt die ausführlich implementierte Fassung nur die „TryParse“-Methode des 32-Bit Integers. Zusätzlich werden hier allerdings noch unsere begrenzten Max- und Minwerte geprüft.   Weiterlesen

Ein eigener Datentyp (Teil 3)

Im letzten Teil (siehe: Ein eigener Datentyp (Teil 2)) haben wir uns um die Implementierung der Grund-Operatoren gekümmert. In diesem Teil wollen wir uns den Vergleichs- sowie Bitoperatoren wenden, welche ebenfalls eine entschiedene Bedeutung für Datentypen haben. Hierfür müssen wir uns jedoch zunächst darüber im Klaren sein, dass wir letztendlich nur eine Einschränkung eines Int32-Datentypes erstellen und deshalb erneut, wie auch bei den Teilen zuvor, von den bereits in Int32 implementierten Methoden Gebrauch machen können. Die hier wohl wichtigsten Operatoren sind jene, die uns die Gleicheit bzw. Ungleichheit bestätigen. Diese würden, da wir eh nur eine Integer-Spezifikation verwenden, wie folgt aussehen:

public static bool operator ==(Int24 value1, Int24 value2)
{
    return value1.Value == value2.Value;
}

public static bool operator !=(Int24 value1, Int24 value2)
{
    return value1.Value != value2.Value;
}

Auch hier ist wieder zu erkennen, dass man es sich als Entwickler oft sehr einfach machen kann. Wobei an dieser Stelle ebenfalls bedacht werden sollte, dass bei einem wirklich eigenen Typen der nicht eine so große Ähnlichkeit mit einem anderen bereits existierenden Typen hat, natürlich viel mehr Aufwand benötigt wird.  Weiterlesen

Ein eigener Datentyp (Teil 2)

Im letzten Teil (siehe: Ein eigener Datentyp (Teil1)) haben wir uns mit dem Grundgerüst unseres Datentypen zufrieden gegeben. Diesmal sollen die Operatoren implementiert werden. Der wohl wichtigste Operator hierfür dürfte der „implicit“-Operator oder auch Zuweisungsoperator sein. Auch hier werden wir wieder zwischen einem normalen Integer-Wert und einem Byte-Array unterscheiden und zwei Implementierungen vornehmen. Trotz der enormen Wichtigkeit dieses Operators ist dieser dennoch sehr simpel zu implementieren.

public static implicit operator Int24(int value)
{
    return new Int24(value);
}

public static implicit operator Int24(byte[] value)
{
    return new Int24(value);
}

Wie im Sourcecode zu sehen wird nur eine neue Instanz eines 24-Bit Integers erzeugt und zurück gegeben. Da wir bereits die nötigen Konstruktoren implementiert haben, haben wir keinen Aufwand mehr mit dieser Methode.  Weiterlesen

Ein eigener Datentyp (Teil 1)

Ein eigener Datentyp kann dann sinnvoll sein, wenn es Voraussetzung ist, Daten in ein Gerät zu schreiben bei denen der Speicherplatz begrenzt ist oder wenn es notwendig ist einen Typen mit einer ganz speziellen Interaktion zu besitzen. Aber wie kann man einen eigenen Datentyp mit sämtlichen Funktionalitäten wie z.B. die eines Integers erzeugen? Nun eigentlich ist das gar nicht so schwer wie man vielleicht denkt, denn .NET bietet dafür bereits einige Schnittstellen an und der andere Teil sind überwiegend die korrekten Operator-Überladungen. In Rahmen dieser Mini-Serie möchte ich einen eigenen 24-Bit großen Integer erstellen, wie man ihn z.B. bei einigen Feldgeräten benötigt. Am Ende des letzten Teils werde ich anschließend den Quellcode sowohl für die Signed, als auch für die Unsigned Variante dieses Typen anhängen. Aber beginnen wir nun mit den Int24-Typen.

Zunächst einmal muss klar sein, was der Unterschied zwischen einer Struktur und einer Klasse ist,  dies wird hier allerdings vorausgesetzt. Und das ist auch eigentlich schon alles was notwendig ist, um den Typen zu erstellen. Wir werden ebenfalls die selben Attribute sowie Interfaces wie ein Int32 verwenden, um die Kompatibilität zu maximieren.  Weiterlesen