.NET Tip #33: Jak fungují desetinná čísla v .NETu a co vlastně umí?

Tomáš Herceg       02.08.2009       C#, VB.NET, .NET Tips       15754 zobrazení

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.

 

hodnocení článku

1 bodů / 1 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback