Waarom resulteert deling van gehele getallen in een geheel getal?

We hebben in Intro to Programming geleerd dat als je twee gehele getallen deelt, je altijd een geheel getal krijgt. Om het probleem op te lossen, maakt u van ten minste één van deze gehele getallen een float.

Waarom begrijpt de compiler niet dat ik wil dat het resultaat een decimaal getal is?

Opmerkingen

  • Misschien wil je dat het resultaat in plaats daarvan een geheel getal is. Hoe kan het het verschil zien?
  • Omdat de C ++ -standaard dat zegt.
  • @soandos: de manier waarop Pascal en Python het allebei doen: ze hebben een aparte operator voor het delen van gehele getallen, maar de standaardoperator voor delen geeft altijd het wiskundig correcte resultaat terug. (of – voor de pedant – zo correct als je kunt krijgen, gegeven de beperkingen van FP-wiskunde.)
  • Hoe weet de compiler dat je wilt dat het resultaat een decimaal getal is? Er zijn geldige redenen om een geheel getal te willen.
  • @soandos In Python, // is de operator voor het delen van gehele getallen en # is een opmerking van één regel. In Pascal is de operator voor het delen van gehele getallen het sleutelwoord div. Beiden werken redelijk goed voor hun respectievelijke taal ges. C doet waarschijnlijk het ergste: één operator die twee totaal verschillende dingen kan doen op basis van een willekeurige context.

Antwoord

Waarom begrijpt de compiler niet dat ik wil dat het resultaat een decimaal getal is?

De C ++ – compiler volgt eenvoudig goed gedefinieerde en deterministische regels zoals uiteengezet in de C ++ – standaard. De C ++ – standaard heeft deze regels omdat de normcommissie besloot het zo te maken.

Ze hadden de norm kunnen schrijven om te zeggen dat wiskunde met gehele getallen resulteert in getallen met drijvende komma, of alleen in het geval van een rest. Dat voegt echter complexiteit toe: ik moet ofwel van tevoren weten wat het resultaat is, of ik moet misschien terugzetten naar een geheel getal als het altijd een float geeft. Misschien wil ik wil een geheel getal .

Een van de kernfilosofieën van C ++ is “u betaalt niet voor wat u niet gebruiktAls je eigenlijk wilt de complexiteit van het mixen van gehele getallen en floats (en de extra CPU-instructies en geheugentoegang die dit met zich meebrengt 1 ), voer dan het type cast uit zoals je in je vraag noemde . Houd anders vast aan de standaard wiskunde met gehele getallen.

Ten slotte kan het mengen van integrale en drijvende-kommavariabelen resulteren in verlies van precisie en soms tot onjuiste resultaten, zoals ik hieronder bespreek. Als je dit wilt, betaal er dan voor: anders schrijft de standaard voor dat compilers zich aan een strikte set regels houden voor het mixen van gegevenstypen. Dit is duidelijk omschreven gedrag: als C ++ ontwikkelaar kan ik dit in de standaard opzoeken en zien hoe het werkt.


Er zijn in wezen drie manieren om te doen wat je probeert te doen, elk met voor- en nadelen.

  • Geheel getal-wiskunde: dit resulteert in het afkappen van resultaten tijdens het delen, zoals je ontdekte. Als u het decimale gedeelte wilt, moet u dat apart behandelen door te delen, de rest op te halen en het decimale gedeelte te behandelen als de rest gedeeld door de deler. Dit is een beetje ingewikkelder van een bewerking en er zijn meer variabelen om te jongleren.

  • Drijvende-komma-wiskunde: dit zal over het algemeen correcte (voldoende) resultaten opleveren voor kleine waarden, maar kan introduceer gemakkelijk fouten met precisie en afronding, vooral als de exponent toeneemt. Als je een groot getal deelt door een klein getal, kun je zelfs een underflow veroorzaken of gewoon een verkeerd resultaat krijgen omdat de schalen van de getallen niet goed met elkaar spelen.

  • Doe je eigen wiskunde. Er zijn klassen die uitgebreide precisie van decimale en rationale getallen afhandelen. Deze zullen doorgaans langzamer zijn dan wiskunde op ingebouwde typen, maar zijn over het algemeen nog steeds vrij snel en bieden wiskunde met willekeurige precisie. Afronding en andere problemen zijn niet automatisch zoals bij IEEE-drijvers, maar je krijgt meer controle en zeker meer nauwkeurigheid.

De sleutel hier is om te kiezen op basis van het probleemdomein . Alle drie de methoden voor het weergeven van cijfers hebben hun eigen voor- en nadelen. Gebruikt u een lusteller? Kies een integraal type. Locaties weergeven in 3D-ruimte? Waarschijnlijk een groep praalwagens. Wilt u geld bijhouden? Gebruik een vast decimaal type.


1 Meest populaire CPU-architecturen (bijv. x86-64 ) hebben aparte sets instructies die werken op verschillende registertypen, zoals integer en floating point, plus extra instructies om te converteren tussen integraal, floating point en verschillende representaties ervan (ondertekend en ongetekend, float en double). Sommige van deze bewerkingen kunnen ook geheugentoegang met zich meebrengen: converteer een waarde en sla deze op in het geheugen (zijn variabele). Wiskunde op CPU-niveau is niet zo eenvoudig als “integer in, float out.”Hoewel het toevoegen van twee gehele getallen een zeer eenvoudige bewerking kan zijn, mogelijk een enkele instructie, kan het combineren van gegevenstypen de complexiteit vergroten.

Opmerkingen

  • U zegt dat de C ++ -standaard bepaalt dat dat gedrag zo moet zijn. Waarom? Zou het niet ‘ het gemakkelijker maken om dingen te zeggen, ” Deling van gehele getallen die niet gelijkmatig deelbaar zijn, resulteert in drijvers, elke andere deling is eerlijk spel. ”
  • @ moonman239 zie mijn bewerkingen.
  • @ moonman239 Niet voor schrijvers van compilers. Veel veelgebruikte CPU-architecturen geven een resultaat met een geheel getal wanneer ze worden gevraagd om te delen met twee gehele getallen. Ze zouden een controle moeten uitvoeren op resultaten die niet geheel zijn en vervolgens moeten overschakelen om de langzamere drijvende komma te gebruiken instructies. Als alternatief hadden ze in gebreke kunnen blijven met deling met drijvende komma en de interesse kwijtgeraakt van degenen die snelle wiskunde wilden, degenen die nauwkeurige wiskunde wilden en degenen die gewend waren aan C. isn ‘ t een optie omdat dat de compatibiliteit met bestaande code zou verbreken.
  • Niet dat je dit als alternatief zou bepleiten, maar het statische type van een uitdrukking maken afhankelijk van de run-time waarden van de operanden die ‘ t werken met C ++ ‘ s statische type systeem.
  • @ moonman239: Een operatie hebben die een ander type produceert, afhankelijk van de waarden van de operanden, is pure waanzin.

Antwoord

Dit komt door de evolutie van hardware. In de begintijd van computers hadden niet alle machines een drijvende-kommagetal, de hardware was eenvoudigweg niet in staat om het begrip drijvende-kommagetal te begrijpen. Drijvende-kommagetallen kunnen natuurlijk worden geïmplementeerd als een software-abstractie, maar dat heeft aanzienlijke nadelen. Alle rekenkundige bewerkingen op deze machines moesten standaard pure rekenkunde met gehele getallen zijn.

En nog steeds is er een duidelijk onderscheid tussen rekenkundige eenheden met gehele en drijvende komma binnen een CPU. Hun operanden worden om te beginnen opgeslagen in afzonderlijke registerbestanden en een integer-eenheid is bedraad om twee integer-argumenten te nemen en een integer-resultaat te produceren dat eindigt in een integer-register. Sommige CPUs vereisen zelfs dat een geheel getal in het geheugen wordt opgeslagen en vervolgens opnieuw wordt geladen in een drijvende-kommaregister, voordat het kan worden gehercodeerd in een drijvende-kommagetal, voordat u er een drijvende-kommadeling op kunt uitvoeren.

Als zodanig was de beslissing die door de C-ontwikkelaars in het allereerste begin van de taal werd genomen (C ++ erfde dit gedrag eenvoudigweg), de enige juiste beslissing die moest worden genomen, en blijft vandaag van waarde: als je wiskunde met drijvende komma nodig hebt, kan het gebruiken. Als je het niet nodig hebt, dan hoef je het niet te doen.

Opmerkingen

  • Het is jammer dat de meeste van de beperkingen die bestonden bij de de creatie van de C ++ -standaard is tegenwoordig behoorlijk achterhaald! Bijvoorbeeld: ” u betaalt niet voor wat u niet gebruikt. ” tegenwoordig wordt hardware als vanzelfsprekend beschouwd en alle gebruikers willen dat ook uitvoering!
  • @ mahen23 Niet alle gebruikers denken zo. Ik werk in een veld waar programmas parallel worden uitgevoerd op duizenden CPU-cores. Op dit gebied is efficiëntie geld, zowel in termen van investeringen als in termen van puur stroomverbruik. Een taal als Java heeft geen schijn van kans op dat gebied, terwijl C ++ dat wel doet.
  • @ mahen23 Nee het is niet ‘ t – of beter, het alleen als je kijkt naar de huidige CPU-architecturen voor desktops en hoger. Er zijn nog steeds veel embedded systemen die ‘ niet of slechts gedeeltelijk drijvende-kommabewerkingen ondersteunen, en zowel C als C ++ blijven deze ondersteunen om de meest efficiënte implementatie mogelijk te maken, afgezien van met behulp van assembler. Trouwens, zelfs hogere niveaus, zoals Python, maken onderscheid tussen integer- en FP-bewerkingen – probeer 10 / 3.

Antwoord

10/2 met gehele getallen geeft u precies 5 – het juiste antwoord.

Met wiskunde met drijvende komma geeft 10/2 misschien het juiste antwoord antwoord *.

Met andere woorden, het is onmogelijk dat getallen met drijvende komma “perfect” zijn op de huidige hardware – alleen wiskunde met gehele getallen kan correct zijn, helaas kan het “geen decimalen uitvoeren, maar er is gemakkelijk werk arounds.

Bijvoorbeeld in plaats van 4/3, doe (4 * 1000) / (3 * 1000) == 1333. Teken gewoon een. in software wanneer je het antwoord aan je gebruiker laat zien (1.333). Dit geeft je een nauwkeurig antwoord, in plaats van een antwoord dat “onjuist is door een aantal decimalen.

Drijvende-kommawiskundige fouten kunnen bij elkaar opgeteld aanzienlijke fouten veroorzaken – alles wat belangrijk is (zoals financiën) zal rekenen met gehele getallen gebruiken .

* het 10/2-voorbeeld zal in feite correct zijn met drijvende-kommaberekeningen, maar je kunt er niet op vertrouwen, veel andere cijfers geven onjuiste resultaten …lees voor meer details: http://http.cs.berkeley.edu/~wkahan/ieee754status/ieee754.ps Het punt is dat u niet op nauwkeurigheid kunt vertrouwen wanneer drijvende kommas zijn betrokken

Opmerkingen

  • IEEE 754-compatibele drijvende-komma-implementaties geven u een exact resultaat voor 10/2. In feite zullen ze u exacte resultaten voor elke bewerking waarbij alleen integer operanden betrokken zijn die een integer resultaat hebben, op voorwaarde dat operanden en resultaat exact kunnen worden weergegeven, welke “klein genoeg” gehele getallen dat kunnen.
  • @ 5gon12eder daar ‘ s het is niet nodig om te kiezen, ik ‘ m probeer gewoon een complex probleem in eenvoudige bewoordingen te beschrijven. Het hele punt van het ondersteunen van niet-gehele getallen is om decimalen ( wat kan worden gedaan met gehele getallen door simpelweg alles te vermenigvuldigen met het aantal decimalen dat u wilt zoals ik demsonstraed).

Answer

Hoewel technisch niet helemaal correct, C ++ wordt nog steeds beschouwd als een superset van C, werd erdoor geïnspireerd en eigende zich als zodanig enkele van zijn eigenschappen toe, waarbij integer-deling er een van is.

C was meestal ontworpen om efficiënt en snel te zijn, en gehele getallen zijn over het algemeen veel sneller dan drijvende kommas, omdat het type integer is gekoppeld aan hardware, terwijl drijvende kommas moeten worden berekend.

Wanneer de / operand twee gehele getallen ontvangt, één aan de linkerkant en een aan de rechterkant, kan het zelfs helemaal niet aan de divisie doen, het resultaat kan worden berekend met behulp van eenvoudige optelling en een lus, waarbij wordt gevraagd hoe vaak de operand aan de rechterkant in de operand aan de linkerkant past.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *