V .NET Frameworku máme obecně 3 datové typy pro reprezentaci desetinných čísel. System.Single, System.Double a System.Decimal. Liší se mimo jiné svojí přesností a speciálními hodnotami indikujícími nekonečno nebo chybu.
Single a Double
Prvním datovým typem s nejmenší přesností je System.Single (v C# má alias float). Zabírá v paměti 32 bitů a podporuje hodnoty do řádu 1038. Kromě nich umí reprezentovat hodnoty -∞, ∞ a NaN (neplatné číslo, chyba ve výpočtu, mimo rozsah atd.). Způsob uložení je popsán standardem IEEE 754, není tedy třeba se bát, že jeho chování závisí na procesoru nebo platformě.
Druhým datovým typem je System.Double, který zabírá v paměti 64 bitů a podporuje hodnoty do řádu 10308. Též podporuje speciální hodnoty pro kladné a záporné nekonečno, i speciální hodnotu NaN.
Jak Single, tak Double jsou v paměti reprezentovány pomocí tří částí - znaménka, mantisy a exponentu. Oněch 32 resp. 64 bitů je rozděleno na tyto tři části. Například pro hodnotu 12345,6789 by bylo znaménko +, mantisa 1,23456789 a exponent 4 (mantisu vynásobíme hodnotou 104). To se samozřejmě musí převést do dvojkové soustavy, ale vzhledem k tomu, že jednotlivé části mají pevně danou velikost, je zřejmé, že není možné reprezentovat všechny hodnoty.
Obecně Single má přesnost asi 7-8 platných číslic, Double asi 14-15. Cokoliv, co vyžaduje větší přesnost, není vhodné do těchto datových typů ukládat, protože to bude zaokrouhlené a nepřesné.
A jak je to se speciálními hodnotami? Najdete je jako konstanty Double.PositiveInfinity (+nekonečno), Double.NegativeInfinity (-nekonečno) a Double.NaN. Obdobně samozřejmě i pro Single.
Kontrolní otázka - co udělá tento kód?
int a = 5;
int b = 0;
int c = a / b;
Samozřejmě vyhodí DivideByZeroException, protože dělit nulou se prostě nesmí. V integerové aritmetice samozřejmě. Pokud totiž datové typy změníme na Double, tak to projde a v c bude hodnota Double.PositiveInfinity.
double a = 5;
double b = 0;
double c = a / b;
Obdobně pokud budeme chtít udělat odmocninu ze záporného čísla (což samozřejmě jde, výsledkem je číslo komplexní, ale to se do double jaksi uložit nedá), bude nám vrácena hodnota Double.NaN. Ta indikuje, že nastala chyba výpočtu, a jakékoliv další operace s touto hodnotou (přičítání, násobení atd.) skončí opět výsledkem NaN.
Pokud si s těmito krajními případy budete hrát, možná brzy narazíte na to, že přestože v proměnné typu Double je hodnota nekonečno, když ji přímo porovnáte s Double.PositiveInfinity, výsledek bude false. To se může stát a je to zcela logické. Porovnávání hodnotových typů prostě vezme bloky paměti obou proměnných a bit po bitu je porovná. Ano, dá se to přepsat, ale u základních datových typů to přepsané není kvůli výkonu a optimalizaci. To, jestli je hodnota NaN či nekonečno se pozná podle toho, že exponent začíná nějakou sekvencí bitů. Co je za těmito bity už je jedno, ale může tam být cokoliv. Nekonečna a NaNy prostě nemají jednu konkrétní reprezentaci, procesor tam nechá ostatní bity tak, jak leží a běží. Proto porovnávání prostě nefunguje a pro zjištění, jestli je proměnná nekonečno nebo NaN slouží funkce Double.IsPositiveInfinity, Double.IsNegativeInfinity a Double.IsNaN. Opět obdobně to funguje u typu Single.
Až tedy budete proměnnou typu Single nebo Double porovnávat s konstantou pro nekonečna pomocí rovnítka a bude vám to házet False i v případě, že v proměnné nekonečno je, je to tím.
Decimal
Posledním datovým typem pro reprezentaci desetinných čísel je System.Decimal. Ten se chová trochu jinak a nabízí výrazně větší přesnost a menší chyby zaokrouhlování. V paměti zabere 128 bitů a je reprezentován jako 96-bitový integer s informací o pozici desetinné čárky. Pojme 29 platných číslic a díky tomu se hodí například pro bankovní výpočty, protože nedochází k tak velkým nepřesnostem jako u předchozích dvou datových typů.
Je však nutno podotknout, že početní operace s ním jsou pomalejší (na rozdíl od Double a Single nejsou tyto operace podporovány procesorem) a nepodporuje také speciální hodnoty jako nekonečna a NaNy.
Závěrem
V novém .NET Frameworku 4 přibude také datový typ BigInteger, který sice nepodporuje desetinná čísla, ale bude umět neomezeně velká celá čísla. Pokud potřebujete neomezeně velká desetinná čísla, musíte si stáhnout J# Runtime Libraries, kde kvůli kompatibilitě s Javou je naimplementován i datový typ BigDecimal.