Každá proměnná má specifický typ. A tento typ určuje, jaké hodnoty může mít proměnná, jaké operace s ní lze provádět a kolik bajtů v paměti bude zabírat. V jazyce C++ jsou definovány následující základní datové typy: boolovský typ bool, celočíselné typy, číselné typy s plovoucí desetinnou čárkou, znakové typy. Podívejme se na tyto skupiny samostatně.
Booleovský typ
Booleovský typ bool může uložit jednu ze dvou hodnot: true (true, true) a false (false, false). Definujme například několik proměnných tohoto typu a zobrazme jejich hodnoty na konzole:
Při výstupu jsou boolovské hodnoty převedeny na 1 (pokud je pravda) a 0 (pokud je nepravda). Obvykle se tento typ s výhodou používá v podmíněných výrazech, o kterých bude řeč později.
Výchozí hodnota pro proměnné tohoto typu je false .
Celočíselné typy
Celá čísla v C++ jsou reprezentována následujícími typy:
- znak podepsaný : Představuje jeden znak. V paměti zabírá 1 bajt (8 bitů). Lze uložit libovolnou hodnotu od -128 do 127
- unsigned char : Představuje jeden znak. V paměti zabírá 1 bajt (8 bitů). Lze uložit libovolnou hodnotu od 0 do 255
- char : Představuje jeden znak ASCII. V paměti zabírá 1 bajt (8 bitů). Může uložit libovolnou hodnotu od -128 do 127 nebo od 0 do 255 Ačkoli tento typ představuje stejný rozsah hodnot jako výše popsaný typ znaku se znaménkem, nejsou ekvivalentní. Typ char je určen k uložení číselného kódu znaku a ve skutečnosti může představovat buď znak se znaménkem, nebo znak bez znaménka v závislosti na konkrétním kompilátoru.
- short : Představuje celé číslo v rozsahu –32768 až 32767. Zabírá 2 bajty (16 bitů) paměti. Tento typ má také aliasy short int, sign short int, sign short.
- unsigned short: Představuje celé číslo v rozsahu 0 až 65535. Zabírá 2 bajty (16 bitů) paměti. Tento typ má také synonymum unsigned short int .
- int: představuje celé číslo. V závislosti na architektuře procesoru může zabírat 2 bajty (16 bitů) nebo 4 bajty (32 bitů). Rozsah mezních hodnot se také může měnit od –32768 do 32767 (se 2 byty) nebo od -2 147 483 648 do 2 147 483 647 (se 4 byty). V každém případě však velikost musí být větší nebo rovna velikosti krátkého typu a menší nebo rovna velikosti dlouhého typu.Tento typ má aliasy podepsané int a podepsané .
- unsigned int : Představuje kladné celé číslo. V závislosti na architektuře procesoru může zabírat 2 bajty (16 bitů) nebo 4 bajty (32 bitů), a proto se rozsah limitních hodnot může lišit: od 0 do 65535 (pro 2 bajty) nebo od 0 až 4 294 967 295 (pro 4 bajty). Má alias nepodepsaný
- long : v závislosti na architektuře může zabírat 4 nebo 8 bajtů a představuje celé číslo v rozsahu −2 147 483 648 až 2 147 483 647 (při 4 bytech) nebo −9 223 372 036 854 775 808 9 223 372 036 854 bajtů). Zabírá 775 bajty (807 bitů) nebo . Má aliasy long int , sign long int a sign long
- unsigned long: Představuje celé číslo v rozsahu 0 až 4 294 967 295. Zabírá 4 bajty (32 bitů) paměti. Má synonymum unsigned long int .
- long long : Představuje celé číslo v rozsahu −9 až +223. Zabírá 372 bajtů (036 bitů) paměti. Má aliasy long long int , sign long long int a sign long long .
- unsigned long long : Představuje celé číslo v rozsahu 0 až 18 446 744 073 709 551 615. Obvykle zabírá 8 bajtů (64 bitů) paměti. Má alias nepodepsaný long long int .
K reprezentaci čísel v C++ používáme celočíselné literály se znaménkem nebo bez znaménka, jako je -10 nebo 10. Definujme například řadu proměnných celočíselných typů a vytiskneme jejich hodnoty do konzole:
#include int main() < znak num1< -64 >; unsigned char num2< 64 >; krátké num3< -88 >; unsigned short num4< 88 >; int num5< -1024 >; unsigned int num6< 1024 >; dlouhé num7< -2048 >; nesignováno dlouhé num8< 2048 >; dlouhé dlouhé num9< -4096 >; unsigned long long num10< 4096 >; std::cout u nebo U. Zadejte literálylong
иlong long
mít přípony L/l и LL/ll odpovídajícím způsobem:#include int main() < unsigned int num6< 1024U >; // U - unsigned int long num7< -2048L >; // L - long unsigned long num8< 2048UL >; // UL - unsigned long long long num9< -4096LL >; // LL - long long unsigned long long num10< 4096ULL >;// ULL - unsigned long long std::cout #include int main() < int num< 1'234'567'890 >; std::coutRůzné číselné soustavy
Ve výchozím nastavení všechny standardní celočíselné literály představují čísla ve známé desítkové soustavě. C++ však umožňuje používat čísla i v jiných číselných soustavách.
Chcete-li označit, že číslo je hexadecimální, je před číslem uvedeno 0x nebo 0X . Například:
int num1; // 0 - v desítkové soustavě int num1< 26xFF >; // 2 - v desítkové soustavě int num0< 255xFFFFFF >; //3 - v desítkové soustavěChcete-li označit, že číslo je osmičkové, před číslem je nula 0 . Například:
int číslo1; // 26 - v desítkové soustavě int num2< 0377 >; // 255 - v desítkové soustavěBinární literály mají předponu 0b nebo 0B:
int num1; // 26 - v desítkové soustavě int num2< 0b11111111 >; // 255 - v desítkové soustavěVšechny tyto typy literálů také podporují přípony U/L/LL:
unsigned int num1< 0b11010U>; // 26 - v desítkové soustavě dlouhé num2< 0377L >; // 255 - v desítkové soustavě bez znaménka long num3< 0xFFFFFFFULL >; //16777215 - v desítkové soustavěČísla s pohyblivou řádovou čárkou
Čísla s plovoucí desetinnou čárkou se používají k ukládání zlomkových čísel v C++. Číslo s plovoucí desetinnou čárkou má dvě části: mantisu a exponent. Obojí může být pozitivní nebo negativní. Velikost čísla je mantisa vynásobená deseti na exponent.
Například číslo 365 lze zapsat jako číslo s plovoucí desetinnou čárkou takto:
3.650000E02Symbol tečky se používá jako oddělovač mezi celým číslem a zlomkem. Mantisa zde má sedm desetinných míst - 3.650000, exponent - dvě číslice 02. Písmeno E znamená exponent, za ním následuje exponent (mocnice deseti), kterým se vynásobí část 3.650000 (mantisa), aby se získala požadovaná hodnota. To znamená, že chcete-li se vrátit k obvyklému desítkovému zápisu, musíte provést následující operaci:
× 3.650000 102 = 365Další příklad - vezměme si malé číslo:
-3.650000E-03V tomto případě máme co do činění s číslem –3.65 × 10 -3, což se rovná –0.00365. Zde vidíme, že v závislosti na hodnotě exponentu desetinná čárka „pluje“. To je vlastně důvod, proč se jim říká čísla s pohyblivou řádovou čárkou.
Nicméně, ačkoli tento zápis umožňuje definovat velmi velký rozsah čísel, ne všechna tato čísla mohou být reprezentována s úplnou přesností; Čísla s plovoucí desetinnou čárkou jsou obecně přibližné reprezentace přesného čísla. Číslo 1254311179 by například vypadalo takto: 1.254311E09. Pokud však přejdeme k desítkovému zápisu, bude to 1254311000. A to není totéž jako 1254311179, protože jsme ztratili tři nižší číslice.
C++ má tři typy pro reprezentaci čísel s plovoucí desetinnou čárkou:
- float : Představuje reálné číslo s plovoucí desetinnou čárkou s jednoduchou přesností v rozsahu +/- 3.4E-38 až 3.4E+38. V paměti zabírá 4 bajty (32 bitů).
- double : Představuje reálné číslo s pohyblivou řádovou čárkou s dvojnásobnou přesností v rozsahu +/- 1.7E-308 až 1.7E+308. V paměti zabírá 8 bajtů (64 bitů).
- long double : Představuje reálné číslo s plovoucí desetinnou čárkou s dvojnásobnou přesností o velikosti alespoň 8 bajtů (64 bitů). V závislosti na velikosti obsazené paměti se rozsah platných hodnot může lišit.
Ve své interní binární reprezentaci se každé číslo s plovoucí desetinnou čárkou skládá z jednoho znaménkového bitu, za nímž následuje pevný počet bitů pro exponent a sada bitů pro uložení mantisy. V plovoucích číslech je 1 bit vyhrazen pro uložení znaménka, 8 bitů pro exponent a 23 pro mantisu, celkem tedy 32 bitů. Mantisa umožňuje určit přesnost čísla ve formě 7 desetinných míst.
Ve dvojitých číslech: 1 znaménkový bit, 11 bitů pro exponent a 52 bitů pro mantisu, tedy celkem 64 bitů. 52bitová mantisa umožňuje specifikovat přesnost na 16 desetinných míst.
U typu long double závisí rozložení na konkrétním kompilátoru a implementaci tohoto datového typu. Většina kompilátorů poskytuje přesnost až 18 - 19 desetinných míst (64bitová mantisa), ale v jiných (jako je Microsoft Visual C++) je long double totéž jako double .
V C++ jsou literály čísel s plovoucí desetinnou čárkou reprezentovány zlomkovými čísly, které používají tečku jako oddělovač desetinných míst:
dvojité číslo;
I když je proměnné přiřazeno celé číslo, používá se tečka, která ukazuje, že přiřazujeme číslo s plovoucí desetinnou čárkou:
double num1< 1 >; // 1 - integer literal double num2< 1. >; //1. - číselný doslov s pohyblivou řádovou čárkou
Číslo 1 zde tedy představuje literál čísla s pohyblivou řádovou čárkou a je v podstatě totéž jako 1.0.
Ve výchozím nastavení jsou všechna čísla s tečkou považována za dvojitá čísla. Chcete-li ukázat, že číslo představuje jiný typ, plovoucí je přípona f / F a dlouhá dvojitá je přípona l / L:
plovák num1< 10.56f >; // float long double num2< 10.56l >; // dlouhý dvojitý
Alternativně můžete také použít exponenciální zápis:
dvojité číslo1< 5E3 >; // 5E3 = 5000.0 double num2< 2.5e-3 >; // 2.5e-3 = 0.0025
Velikosti datových typů
Při výpisu datových typů byla uvedena velikost, kterou zaujímá v paměti. Jazykový standard však specifikuje pouze minimální hodnoty, které by měly existovat. Například pro typy int a short je minimální hodnota 16 bitů, pro typ long - 32 bitů, pro typ long double - 64 bitů. V tomto případě nesmí být velikost dlouhého typu menší než velikost typu int a velikost typu int nesmí být menší než velikost krátkého typu a velikost dlouhého double typu musí být být nejméně dvojnásobek. A vývojáři kompilátorů mohou volit limity velikosti pro typy nezávisle na hardwarových možnostech počítače.
Například kompilátor Windows g++ používá 16 bajtů pro dlouhé dvojité. A kompilátor ve Visual Studiu, který také běží na Windows, a clang++ na Windows pro dlouhé dvojité použití 8 bajtů. To znamená, že i v rámci stejné platformy mohou mít různé kompilátory různé přístupy k velikostem určitých datových typů. Ale obecně se používají velikosti, které jsou uvedeny výše při popisu datových typů.
Jsou však situace, kdy je potřeba přesně znát velikost určitého typu. A za tímto účelem má C++ operátor sizeof(), který vrací velikost paměti v bajtech, kterou proměnná zabírá:
#include int main() < dlouhé dvojité číslo ; std::coutsizeof(číslo) = 16Typy postav
C++ má následující typy datových znaků:
- char : Představuje jeden znak ASCII. V paměti zabírá 1 bajt (8 bitů). Lze uložit libovolnou hodnotu od -128 do 127 nebo od 0 do 255
- wchar_t : Představuje široký znak. Na Windows zabere 2 bajty (16 bitů) paměti, na Linuxu 4 bajty (32 bitů). Lze uložit libovolnou hodnotu z rozsahu od 0 do 65 535 (pro 2 bajty) nebo od 0 do 4 294 967 295 (pro 4 bajty)
- char8_t : Představuje jeden znak Unicode. Zabírá 1 bajt v paměti. Lze uložit libovolnou hodnotu od 0 do 256
- char16_t : Představuje jeden znak Unicode. V paměti zabírá 2 bajty (16 bitů). Lze uložit libovolnou hodnotu od 0 do 65 535
- char32_t : Představuje jeden znak Unicode. V paměti zabírá 4 bajty (32 bitů). Lze uložit libovolnou hodnotu od 0 do 4 294 967 295
spálit
Proměnná typu char ukládá číselný kód jednoho znaku a zabírá jeden bajt. Jazykový standard C++ nespecifikuje kódování znaků, které bude použito pro znaky typu char, takže výrobci kompilátorů mohou zvolit libovolné kódování, ale obvykle je to ASCII.
Jako hodnotu může mít proměnná typu char jeden znak v jednoduchých uvozovkách nebo číselný kód znaku:
V tomto případě budou mít proměnné a1 a a2 stejnou hodnotu, protože 65 je číselný kód pro znak "A" v ASCII tabulce. Při výstupu na konzolu pomocí cout se ve výchozím nastavení zobrazuje symbol.
C++ navíc umožňuje používat speciální escape sekvence, kterým předchází lomítko a které jsou interpretovány speciálním způsobem. Například "n" představuje nový řádek a "t" představuje tabulátor.
ASCII je však obecně vhodný pro znakové sady jazyků, které používají latinku. Pokud ale potřebujete pracovat se znaky pro více jazyků současně nebo se znaky v jiných jazycích než v angličtině, 256znakové kódy nemusí stačit. A v tomto případě platí Unicode.
Unicode je standard, který definuje sadu znaků a jejich kódových bodů, stejně jako několik různých kódování pro tyto kódové body. Nejčastěji používaná kódování jsou UTF-8, UTF-16 a UTF-32. Rozdíl mezi těmito dvěma je v tom, jak je reprezentován bod kódu postavy; číselná hodnota kódu pro jakýkoli znak zůstává stejná v jakémkoli z kódování. Hlavní rozdíly:
- UTF-8 představuje znak jako sekvenci proměnné délky jednoho až čtyř bajtů. Znaková sada ASCII se v UTF-8 zobrazuje jako jednobajtové kódy, které mají stejné hodnoty kódu jako v ASCII. UTF-8 je zdaleka nejoblíbenější kódování Unicode.
- UTF-16 představuje znaky jako jednu nebo dvě 16bitové hodnoty.
- UTF-32 představuje všechny znaky jako 32bitové hodnoty
C++ má čtyři typy pro ukládání znaků Unicode: wchar_t, char8_t, char16_t a char32_t (char16_t a char32_t byly přidány v C+11 a char8_t v C++20).
wchar_t
Typ wchar_t je základní typ určený pro znakové sady, které jsou větší než jeden bajt. Ve skutečnosti odtud pochází jeho název: wchar_t - široký (široký) char. pochází ze širokého znaku, protože znak je "širší" než běžný jednobajtový znak. Hodnoty wchar_t jsou definovány stejně jako znaky char, kromě toho, že jim předchází znak "L":
wchar_t a1 ;
Můžete také předat kód znaku
wchar_t a1 ;
Hodnota uzavřená v jednoduchých uvozovkách je hexadecimální kód znaku. Zpětné lomítko označuje začátek sekvence escape a x za zpětným lomítkem označuje, že kód je hexadecimální.
Stojí za zvážení, že pro výstup znaků wchar_t do konzole byste neměli používat std::cout , ale std::wcout stream:
V tomto případě může proud std::wcout pracovat s char i wchar_t. A proud std::cout pro proměnnou wchar_t vypíše svůj číselný kód místo znaku.
Problém s typem wchar_t je v tom, že jeho velikost je vysoce závislá na implementaci a použitém kódování. Kódování obvykle odpovídá preferovanému kódování cílové platformy. V systému Windows je tedy wchar_t obvykle 16 bitů široký a je kódován pomocí UTF-16. Většina ostatních platforem nastavuje velikost na 32 bitů a jako kódování používá UTF-32. Na jedné straně vám to umožňuje být konzistentnější s konkrétní platformou. Ale na druhou stranu to znesnadňuje psaní kódu, který je přenosný na různé platformy. Proto se obecně často doporučuje používat typy char8_t, char16_t a char32_t. Hodnoty těchto typů jsou navrženy pro ukládání znaků UTF-8, UTF-16 nebo UTF-32 a jejich velikosti jsou stejné na všech běžných platformách.
Pro definování znaků typů char8_t, char16_t a char32_t se používají předpony u8, u a U:
char8_t c< u8'l' >; char16_t d< u'l' >; char32_t e< U'o' >;
Stojí za zmínku, že neexistují žádné vestavěné nástroje jako std:cout/std:wcout pro výstup hodnot char8_t/char16_t/char32_t do konzole.
automatický specifikátor
Někdy může být obtížné určit typ výrazu. V tomto případě můžete nechat kompilátor odvodit typ samotného objektu. A k tomu slouží automatický specifikátor. Navíc, pokud definujeme proměnnou pomocí auto specifikátoru, musí být tato proměnná inicializována nějakou hodnotou:
automatické číslo = 5; // číslo je typu int auto sum ; // součet je typu double auto distance ; // vzdálenost je typu unsigned long
Na základě přiřazené hodnoty kompilátor odvodí typ proměnné. Neinicializované proměnné s automatickým specifikátorem nejsou povoleny:
Vzhledem k reálnému datovému typu s maximální hodnotou 10^20 musíte zjistit, kolik bitů je alokováno pro exponent. Můžete prosím vysvětlit.
Stopa
zeptal se 23. října 2017 ve 9:16
75 2 2 bronzové odznaky
3 odpovědi 3
Řadit: Obnovit výchozí
Skutečná čísla s pohyblivou řádovou čárkou podporovaná na úrovni procesoru jsou popsána v mezinárodním standardu IEEE 754.
Takže hlavní dva typy pro jakékoli výpočty jsou jednoduchá přesnost (jednoduchá přesnost) a dvojitá přesnost (dvojitá přesnost) s pohyblivou řádovou čárkou (čísla s pohyblivou řádovou čárkou).
Pro reprezentaci s jednoduchou přesností je alokováno 32 bitů a pro dvojnásobnou přesnost 64 bitů.
V bitech v jednoduchých číslech s přesností:
| 1 бит под знак | 8 бит экспоненты | 23 бита мантиссы |
Pro dvojnásobnou přesnost:
| 1 бит под знак | 11 бит экспоненты | 52 бита мантиссы |
#include typedef union < float f; struct < unsigned int mantisa : 23; unsigned int exponent : 8; unsigned int sign : 1; >parts; > float_cast; int main(void) < float_cast d1 = < .f = 0.15625 >; printf("sign = %xn", d1.parts.sign); printf("exponent = %xn", d1.parts.exponent); printf("mantisa = %xn", d1.parts.mantisa); >
Stopa
odpověděl 23. října 2017 v 9:26
76 1 1 bronzový odznak
Co ten 10bajtový dlouhý double?
23. října 2017 v 9:32 hodin
Mluvíte o standardních reprezentacích. Ale jejich horní hranice není v žádném případě 10^20.
23. října 2017 v 9:33 hodin
Zkoušel jsem to spočítat - zdálo se, že to funguje, ale něco je špatně s Long double - můžeš se podívat?
23. října 2017 v 10:18 hodin
Formálně mohou existovat různé názory.
Pro jistotu budeme uvažovat normalizovanou formu IEEE 745, kdy mantisa nabývá hodnot od 1 do 2 a pořadí je posunuto o polovinu velikosti.
Nechť b bitů je přiděleno pro objednávku; pak se maximální hodnota řádu získá jako 2 b-1. Celkem Podívejme se dále - pro 7 bitů to vyjde 64, maximální počet je 2 * 2 64 - přibližně 3.6 * 10 19 . Trochu chybí. To znamená, že 8 bitů je pravda, ale výsledkem bude 2*2 128 ~ 6.8*10 38 .
Proto mám pocit, že od vás očekávají 7bitovou odpověď 🙂
Pokud však například změníte posun v reprezentaci pořadí nebo považujete reprezentaci mantisy za nenormalizovanou, mohou okamžitě existovat další možnosti.
Stopa
odpověděl 23. října 2017 v 9:32
221k 15 15 zlatých odznaků 120 120 stříbrných odznaků 233 233 bronzových odznaků
A podívejte se na mou odpověď - nechápu, proč se long double nekonverguje.
23. října 2017 v 10:17 hodin
@Qwertiy Ne, nestačím okamžitě říct, co je co (proto píšu svůj "Myslím, že ano" :)), ale teď není čas to důkladně pochopit. Pocit je takový, že long double je kluzký typ a nikdo přesně neví, kolik bajtů obsahuje. O! Můžete to cítit pro jeho význam - zkazit to po kousku a podívat se. Pojďme se na to nyní podívat.
23. října 2017 v 10:40 hodin
@Qwertiy Tady jsem pomalu podělal dlouhý double - a vůbec nereagoval na první bajty! - viz ideone.com/Nhg97d - tak opravdu toto je 10 bajtů.
23. října 2017 v 10:46 hodin
Vzhledem k reálnému datovému typu musíte zjistit, kolik bitů je alokováno pro exponent.
Existuje zajímavý způsob: je známo, že všechna použitá reálná čísla používají 1 bit na znaménko, mají mantisu a exponent. Velikost typu snadno zjistíme pomocí sizeof , ale počet bitů v mantise a exponentu je trochu složitější.
Vezmeme číslo 1 a dělíme ho 2, dokud nedostaneme 0. V jakém okamžiku to vyjde? Nejprve bude číslo normalizováno a mantisa bude obsahovat 0 a exponent bude klesající. Poté exponent dosáhne své minimální hodnoty, číslo přejde do denormalizované podoby a jedna jednotka se proplíží celou mantisou. Když tento zmizí za přesností typu, číslo se změní na 0. Za zmínku také stojí, že existuje o 1 více kladných exponentů než záporných, což nám dává +1 bit a možnost počítat kladné mocniny o 1 více než negativních beztrestně.
Když se to pokusíme použít, dostaneme následující kód: https://ideone.com/YuIWNc
#include #define COUNT(typ, vysledek_m, vysledek_e) do < typ x = 1, exp; nepodepsané res, e; pro (res=0; x!=0; ++res) x/=2.; for (exp=1,e=0; exp*2while(0) int main(void)
S M E SZ float: 1 23 8 32 double: 1 52 11 64 long double: 1 63 15 128
Jak vidíte, velikost exponentu se shoduje s Wikipedií: 8, 11 a 15. Velikost mantisy je trochu složitější - float a double jsou stejné (ve znaménku je zahrnut jen 1 bit), ale pro z nějakého důvodu má long double 64 bitů, místo očekávaných 113. S Na druhou stranu, pokud si pamatuji, plusy by měly používat 10 bajtů pro dlouhý double, a ne 16. Obecně je 10 bajtů vhodné, ale sizeof ukazuje 16. Pokud je v kódu chyba, napište do komentářů a já ji opravím.