Proč má celočíselné dělení za následek celé číslo?

V úvodu do Programování jsme se dozvěděli, že pokud rozdělíte dvě celá čísla, vždy získáte celé číslo. Chcete-li problém vyřešit, udělejte alespoň jedno z těchto celých čísel plovoucí.

Proč kompilátor nerozumí, chci, aby výsledkem bylo desítkové číslo?

Komentáře

  • Možná chcete, aby výsledkem bylo celé číslo. Jak to pozná rozdíl?
  • Protože to říká standard C ++.
  • @soandos: Způsob, jakým to Pascal i Python dělají: mají zřetelný operátor celočíselného dělení, ale operátor standardního dělení vždy vrátí matematicky správný výsledek. (Nebo – pro pedantický – tak správný, jak můžete dostat vzhledem k omezení FP matematiky.)
  • Jak by překladač věděl, že chcete, aby výsledkem bylo desítkové číslo? Existují platné důvody, proč požadovat celé číslo.
  • @soandos V Pythonu, // je operátor celočíselného dělení a # je jednořádkový komentář. V Pascalu je operátorem celočíselného dělení klíčové slovo div. Oba fungují docela dobře pro svou příslušnou jazyk ges. C dělá pravděpodobně to nejhorší, co je možné: jeden operátor, který může dělat dvě zcela odlišné věci na základě libovolného kontextu.

Odpovědět

Proč překladač nechápe, že chci, aby výsledkem bylo desítkové číslo?

Kompilátor C ++ jednoduše dodržuje dobře definovaná a deterministická pravidla stanovená ve standardu C ++. Standard C ++ má tato pravidla, protože výbor pro standardy se rozhodl to udělat tímto způsobem.

Mohli napsat standard, aby řekli, že celočíselná matematika vede k číslům s plovoucí desetinnou čárkou, nebo to dělá pouze v případ zbytku. To však přidává na složitosti: buď potřebuji předem vědět, jaký je výsledek, nebo možná převést zpět na celé číslo, pokud vždy dává plovák. Možná chci celé číslo .

Jednou z hlavních filozofií C ++ je „neplatíte za to, co nepoužíváte . “ Pokud skutečně chcete složitost míchání celých čísel a plováků (a další pokyny k CPU a přístup k paměti s tím souvisí 1 ), proveďte typ cast, jak jste zmínili ve své otázce . Jinak se držte standardní celočíselné matematiky.

Nakonec smíchání proměnných s integrální a plovoucí desetinnou čárkou může mít za následek ztrátu přesnosti a někdy nesprávné výsledky, jak diskutuji níže. Pokud to chcete, pak za to zaplatte: jinak standard určuje, že kompilátoři dodržují přísnou sadu pravidel pro míchání datových typů. Toto je dobře definované chování: jako vývojář v C ++ to můžu vyhledat ve standardu a zjistit, jak to funguje.


Existují v zásadě tři způsoby, jak dělat to, co se snažíte udělat, každý s výhodami a nevýhodami.

  • Celočíselná matematika: výsledkem bude zkrácení výsledků během dělení, jak jste zjistili. Pokud chcete desetinnou část, musíte s ní zacházet odděleně dělením, získáním zbytku a zacházením s desetinnou částí jako se zbytkem děleným dělitelem. Jedná se o trochu složitější operaci a má více proměnných, s nimiž je možné žonglovat.

  • Matematika s plovoucí desetinnou čárkou: obecně to vytvoří správné (dostatečné) výsledky pro malé hodnoty, ale může snadno zavádět chyby s přesností a zaokrouhlováním, zvláště když se exponent zvětšuje. Pokud vydělíte velké číslo malým číslem, můžete dokonce způsobit podtečení nebo jednoduše získat špatný výsledek, protože stupnice čísel mezi sebou nehrají dobře.

  • Udělejte si vlastní matematiku. Existují třídy, které zpracovávají rozšířenou přesnost desetinných a racionálních čísel . Obvykle budou pomalejší než matematika na vestavěných typech, ale obecně jsou stále docela rychlé a poskytují matematiku s libovolnou přesností. Zaokrouhlování a další problémy nejsou automatické, jako jsou tomu u plováků IEEE, ale získáte větší kontrolu a určitě větší přesnost.

Klíčem je výběr na základě problémové domény . Všechny tři metody reprezentace čísel mají své vlastní výhody a nevýhody. Používáte čítač smyček? Vyberte integrální typ. Reprezentujete místa ve 3D prostoru? Pravděpodobně skupina plováků. Chcete sledovat peníze? Použijte typ s pevným desetinným místem.


1 Nejoblíbenější architektury CPU (např. x86-64 ) bude mít samostatné sady instrukcí, které fungují na různých typech registrů, jako je celé číslo a plovoucí desetinná čárka, plus další instrukce pro převod mezi integrálem, plovoucí desetinnou čárkou a jejich různé reprezentace (podepsané a nepodepsané, float a double). Některé z těchto operací mohou také vyžadovat přístup do paměti: převést hodnotu a uložit ji do paměti (její proměnné). Matematika na úrovni CPU není tak jednoduchá jako „integer in, float out.„I když přidání dvou celých čísel může být velmi jednoduchá operace, možná jediná instrukce, míchání datových typů může zvýšit složitost.

Komentáře

  • Říkáte, že standard C ++ stanoví, že by to tak mělo být. Proč? Nebylo by to ‚ jednodušší, “ Rozdělení celých čísel, která nejsou rovnoměrně dělitelná, má za následek floats, jakékoli jiné rozdělení je spravedlivá hra. “
  • @ moonman239 zobrazit moje úpravy.
  • @ moonman239 Není určeno pro autory překladačů. Mnoho běžně používaných architektur CPU poskytuje celočíselný výsledek, když je požadováno dělení na dvě celá čísla. Museli by implementovat kontrolu neceločíselných výsledků a poté přepnout na použití pomalejší plovoucí desetinné čárky instrukce. Alternativně by mohli selhat na dělení s plovoucí desetinnou čárkou a ztratit zájem těch, kteří chtěli rychlou matematiku, těch, kteří chtěli přesnou matematiku, a těch, kteří byli zvyklí na C. není ‚ možnost, protože by to narušilo kompatibilitu s existujícím kódem.
  • Ne, že byste to obhajovali jako alternativu, ale vytvořili byste statický typ výrazu závisí na hodnotách běhu operandů, které nebudou pracovat se systémem statického typu C ++ ‚.
  • @ moonman239: Mít operaci, která produkuje jiný typ v závislosti na hodnotách operandů, je naprosté šílenství.

Odpověď

Důvodem je vývoj hardwaru. V počátcích počítačů neměly všechny stroje jednotku s plovoucí desetinnou čárkou, hardware prostě nebyl schopen pochopit pojem čísla s plovoucí desetinnou čárkou. Čísla s plovoucí desetinnou čárkou lze samozřejmě implementovat jako softwarovou abstrakci, ale to má značné nevýhody. Veškerá aritmetika na těchto počítačích musela být ve výchozím nastavení čistě celočíselná aritmetika.

A ještě dnes existuje v rámci CPU pevný rozdíl mezi celočíselnými a plovoucími řádovými čísly. Jejich operandy jsou pro začátek uloženy v samostatných souborech registrů a celočíselná jednotka je zapojena, aby převzala dva celočíselné argumenty a vytvořila celočíselný výsledek, který skončí v celočíselném registru. Některé CPU dokonce vyžadují, aby byla celočíselná hodnota uložena do paměti a poté znovu načtena zpět do registru s plovoucí desetinnou čárkou, než bude možné ji překódovat na číslo s plovoucí desetinnou čárkou, než na ní můžete provést dělení s plovoucí desetinnou čárkou.

Jako takové bylo rozhodnutí učiněné vývojáři jazyka C na samém začátku jazyka (C ++ toto chování jednoduše zdědilo), jediné vhodné rozhodnutí, které je třeba učinit, a dnes má hodnotu: Pokud potřebujete matematiku s pohyblivou řádovou čárkou, můžete může to použít. Pokud to nepotřebujete, nemusíte.

Komentáře

  • Je smutné, že většina omezení, která existovala na tvorba standardu C ++ je dnes docela zastaralá! Například: “ neplatíte za to, co nepoužíváte. “ v dnešní době je hardware považován za samozřejmost a všichni uživatelé chtějí provedení!
  • @ mahen23 Ne všichni uživatelé takto uvažují. Pracuji v oblasti, kde jsou programy spuštěny na tisících procesorových jader paralelně. V této oblasti jsou efektivitou peníze, a to jak z hlediska investic, tak z hlediska samotné spotřeby energie. Jazyk jako Java nemá v této oblasti ducha šance, zatímco C ++ ano.
  • @ mahen23 Ne, není to ‚ t – nebo lépe, že pouze pokud se podíváte na aktuální architektury CPU pro stolní počítače a vyšší. Stále existuje mnoho vestavěných systémů, které ‚ t nebo jen částečně podporují operace s plovoucí desetinnou čárkou a C i C ++ je nadále podporují, aby poskytly co nejefektivnější implementaci pomocí assembleru. BTW, ještě vyšší jazyky, jako je Python, rozlišují mezi celočíselnými a FP operacemi – zkuste 10 / 3.

Odpovědět

10/2 s celými čísly vám dává přesně 5 – správná odpověď.

S matematikou s plovoucí desetinnou čárkou 10/2 může dát správnou answer *.

Jinými slovy je nemožné, aby čísla s plovoucí desetinnou čárkou byla na současném hardwaru „dokonalá“ – správná může být pouze celočíselná matematika, bohužel nemůže dělat desetinná místa, ale práce je snadná kolem.

Například místo 4/3 udělejte (4 * 1000) / (3 * 1000) == 1333. Při zobrazení odpovědi uživateli (1,333) jednoduše nakreslete a. v softwaru. Získáte tak přesnou odpověď namísto odpovědi, která je nesprávná o několik desetinných míst.

Matematické chyby s plovoucí desetinnou čárkou se mohou sčítat a způsobovat významné chyby – všechno důležité (například finance) bude používat celočíselnou matematiku .

* příklad 10/2 bude ve skutečnosti správný s matematikou s plovoucí desetinnou čárkou, ale nemůžete se na to spolehnout, mnoho dalších čísel dává nesprávné výsledky …pro více podrobností si přečtěte: http://http.cs.berkeley.edu/~wkahan/ieee754status/ieee754.ps Jde o to, že se nemůžete spolehnout na přesnost, kdykoli jsou plovoucí body zúčastněné

Komentáře

  • Implementace plovoucí desetinné čárky vyhovující IEEE 754 vám poskytnou přesný výsledek za 10/2. Ve skutečnosti vám poskytnou přesné výsledky pro jakoukoli operaci zahrnující pouze celočíselné operandy, které mají celočíselný výsledek za předpokladu, že operandy a výsledek lze přesně reprezentovat, což mohou „dostatečně malá“ celá čísla.
  • @ 5gon12eder tam ‚ s není třeba vybírat, jen se ‚ snažím popsat složitý problém jednoduše. Celý bod podpory neceločíselných hodnot je mít desetinná místa ( což lze provést pomocí celých čísel jednoduchým vynásobením všeho počtem požadovaných desetinných míst, jak jsem uvedl (demsonstraed).

Odpověď

Ačkoli to technicky není úplně správné, C ++ je stále považován za nadmnožinu jazyka C, inspiroval se jím a jako takový si přivlastnil některé jeho vlastnosti, přičemž celočíselné dělení je jednou z nich.

C bylo většinou navrženo tak, aby bylo efektivní a rychlé, a celá čísla jsou obecně mnohem rychlejší než plovoucí body, protože celočíselný typ je vázán na hardware, zatímco plovoucí body je třeba vypočítat.

Když operand / obdrží dvě celá čísla, jedno na levé straně a jeden na pravé straně to nemusí vůbec dělat dělení, výsledek lze vypočítat pomocí jednoduchého sčítání a smyčky s dotazem, kolikrát se operand na pravé straně vejde do operandu na levé straně.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *