.NET Tip #34: Jak fungují aritmetické operátory a kdy se musí v C# přetypovávat

Tomáš Herceg       07.08.2009       C#, VB.NET, .NET Tips       13021 zobrazení

Na následující .NET Tip mě přivedl nedávný dotaz v diskusích o dělení v SQL. My se budeme bavit o VB.NET a C# a ukážeme si, jak tam fungují aritmetické operace, zejména dělení, protože tam je to krásně vidět.

Číselné literály

Pokud někam do kódu napíšu jako číslo 15, bere se to automaticky jako hodnota typu System.Int32 (tedy Integer resp. int). Přestože se patnáctka vejde do typu Byte, je to prostě Integer, protože ten má velikost 32 bitů a procesory s ním pracují nejrychleji. Pokud by číslo bylo větší a do intu se nevešlo, kompilátor to bude brát jako hodnotu typu System.Int64, tedy Long. Jakmile za číslo dopíšeme desetinnou část, konstanta se změní na datový typ System.Double, takže 15.0 je prostě double.

Co je to operátor?

Operátor je vlastně úplně normální funkce, která na vstupu dostane operandy a vrací výsledek. Podívejme se třeba na operátor dělení, ve VB.NET i v C# máme operátor /. V každém jazyce se chová ale trochu jinak.

Ve VB.NET má operátor / funkci obyčejného dělení, jehož výsledkem je vždy desetinné číslo. Pro celočíselné dělení máme speciální operátor \. Naproti tomu v C# je tomu jinak, tam operátor / slouží jak pro normální dělení s desetinným výsledkem, tak i pro dělení celočíselné.

Ve VB.NET je operátor dělení definován takto:

Datové typy operandů (VB.NET) Výsledek
Oba výrazy celočíselné Double
Oba výrazy typu Decimal Decimal
Oba výrazy typu Single Single
Alespoň jeden výraz typu Single nebo Double Double

V C# je operátor dělení definován takto:

Datové typy operandů (C#) Výsledek
Oba výrazy Byte, SByte, Int16, UInt16, Int32 nebo UInt32 Int32/UInt32
Oba výrazy celočíselné, alespoň jeden typu Int64 nebo UInt64 Int64/UInt64
Oba výrazy typu Decimal Decimal
Oba výrazy typu Single Single
Alespoň jeden výraz typu Single nebo Double Double

Tady vidíme, že při dělení celých čísel je ve VB.NET výsledkem číslo desetinné, zatímco v C# je to opět celé číslo.

Jak fungují konverze?

Číselné datové typy v .NET Frameworku mají za jistých okolností povoleny automatické konverze. To znamená, že je možné je přiřadit do proměnné jiného typu bez příslušného přetypování. Těchto konverzí jsou dva druhy, které se od sebe liší.

Bez obav můžeme přetypovat Int32 na Int64 a nemusíme se bát, že o nějakou informaci přijdeme. Stejně tak můžeme přetypovat i Single na Double, Int32 na Decimal nebo třeba Byte na Single. Takové konverze se nazývají widening conversions protože všechny hodnoty prvního datového typu je možné reprezentovat v datovém typu druhém (všechny možné hodnoty typu Byte můžeme uložit do Single). Navíc tyto konverze probíhají automaticky a přetypovávat nemusíme. Mimochodem přiřazení FileStreamu do proměnné typu Stream je samozřejmě také widening conversion, opět nemusíme přetypovávat a každý FileStream můžeme bezpečně uložit do proměnné typu Stream (opačně ne!).

Opačné konverze se nazyvají narrowing conversions a u nich už může dojít ke ztrátě nějaké informace nebo k chybě. Je to třeba přiřazení proměnné typu Double do proměnné typu Int32 (provádíme zaokrouhlení, ztrácíme informaci), nebo Int64 do Int32. Pokud se velké číslo do Int32 nevejde, v C# se ve standardním nastavení horní bity se oříznou, takže v cílové proměnné dostaneme jiné číslo. VB.NET standardně kontroluje chyby přetečení, takže skončíme s výjimkou OverflowException. Kontrola přetečení se jak u C#, tak u VB.NET dá na úrovni aplikace zapnout nebo vypnout nastavením kompilátoru, v C# ji můžete navíc vynutit nebo potlačit pro výraz nebo blok kódu klíčovými slovy checked a unchecked.

Stejně tak je narrowing conversion i přetypování Streamu na FileStream, které klidně může skončit chybou, protože každý Stream nemusí být FileStream.

Jak je to ve VB.NET a v C#?

V C# se widening convertions provádí automaticky a přetypovávat nemusíme. Klidně můžeme napsat double a = 5; a už víme, že 5 je sice hodnota typu Int32, ale převod na double je právě widening conversion a provede se automaticky.

Naopak narrowing conversions se automaticky neprovádí a přetypovávat musíme. Takže pokud napíšeme float a = 15.3; a budeme se divit, proč to nefunguje, je to proto, že 15.3 je hodnota typu double a my se ji snažíme narvat do floatu, tedy datového typu s menší přesností. Možnosti jsou dvě - buď přetypovat (float a = (float)15.3;) anebo použít u literálu suffix f, který řekne, že tahle konstanta bude typu float. Zápis by vypadal takto: float a = 15.3f;.

Obdobně musíme přetypovávat při FileSteam fs = (FileStream)s, pokud s je typu Stream. To může i skončit chybou, pokud v s bude třeba NetworkStream.

Ve VB.NET se ve výchozím nastavení oba typy konverzí provádějí automaticky a přetypovávat nemusíme. Pokud chceme VB donutit, aby narrowing konverze kontroloval a nutil nás do přetypování jako C#, stačí v kompilátoru zapnout Strict Mode.

Jak teda funguje to dělení?

Pokud jste všechno pochopili správně, měli byste být schopni odpovědět na tuto otázku:

1. Jaký bude datový typ proměnné retVal, jakou bude mít hodnotu a proč? Kód je ve VB 9 se zapnutou volbou Option Infer (default), takže retVal nebude typu Object, ale datový typ se určí při kompilaci automaticky podle výsledku přiřazovaného výrazu.

Dim retVal = 15 / 4

2. Jaký bude datový typ proměnné retVal, jakou bude mít hodnotu a proč? Kód je v C# 3, datový typ proměnné opět určí kompilátor podle výsledku výrazu.

var retVal = 15 / 4;

Závěrem

Doufám, že vám tento .NET Tip pomohl zase trochu nahlédnout do toho, jak v .NETu věci fungují. No a já si jdu zahrát golf.

 

hodnocení článku

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

 

Nový příspěvek

 

Diskuse: .NET Tip #34: Jak fungují aritmetické operátory a kdy se musí v C# přetypovávat

Díky za článek, už mi je jasnější jak to funguje:)

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: .NET Tip #34: Jak fungují aritmetické operátory a kdy se musí v C# přetypovávat

Nevím co bylo konkrétně myšleno větou "pokud se číslo do Int32 nevejde, horní bity se oříznou, takže dostaneme jiné číslo", ale pokud se snažíte přiřadit do proměnné větší číslo než je její kapacita, program se ani nepřeloží (pokud se jedná o hodnotu známou v době kompilace), za běhu dojde k vyjímce OverflowException.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Obecně není pravda. Záleží, jak byl zdrojový kód přeložen. Viz. tyto dva přepínače kompilátorů:

/checked pro C# http://msdn.microsoft.com/en-us/library/...

/removeintchecks pro VB.NET http://msdn.microsoft.com/en-us/library/...

V C# lze navíc chování ad-hoc změnit pomocí statementu/operátoru checked a unchecked:

http://msdn.microsoft.com/en-us/library/...

http://msdn.microsoft.com/en-us/library/...

nahlásit spamnahlásit spam 2 / 2 odpovědětodpovědět

Díky za doplnění. Zapomněl jsem uvést, že to s tím oříznutím bitů platí ve standardním nastavení v C#, kde je kontrola přetečení vypnutá. Ve VB.NET je standardně zapnutá.

V C# lze pro určitý blok kontrolu přetečení vynutit nebo potlačit přes klíčová slova checked a unchecked.

V obou jazycích to jde na úrovni celé aplikace nastavit přpínačem kompilátoru.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Diskuse: .NET Tip #34: Jak fungují aritmetické operátory a kdy se musí v C# přetypovávat

Díky za výborný snippet, hlavně za tu část o typech konverzí. Dlouho dobu jsem si lámal hlavu s tím, k čemu klíčová slova Widening a Narrowing jsou, z anglický textů jsem to nikdy nepochopil.

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
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