Hvorfor resulterer heltallsdeling i et heltall?

Vi lærte i introduksjonen til programmering at hvis du deler to heltall, får du alltid et heltall. For å løse problemet, gjør minst ett av disse heltallene en flottør.

Hvorfor forstår ikke kompilatoren at jeg vil at resultatet skal være et desimaltall?

Kommentarer

  • Kanskje du vil at resultatet skal være et heltall i stedet. Hvordan kan det se forskjellen?
  • Fordi C ++ -standarden sier det.
  • @soandos: Slik Pascal og Python begge gjør det: har en distinkt heltalsdivisjonsoperator, men standarddivisjonsoperatøren returnerer alltid det matematiske riktige resultatet. (Eller – for pedanten – så riktig som du kan få gitt begrensninger av FP-matematikk.)
  • Hvordan vet kompilatoren at du vil at resultatet skal være et desimaltall? Det er gyldige grunner til å ønske et heltall.
  • @soandos I Python, // er operatøren for heltall og # er en enkeltlinjekommentar. I Pascal er operatøren for heltallsdivisjon nøkkelordet div. Begge fungerer ganske bra for deres respektive språk ges. C gjør sannsynligvis det verste som er mulig: en operatør som kan gjøre to helt forskjellige ting basert på vilkårlig sammenheng.

Svar

Hvorfor forstår ikke kompilatoren at jeg vil at resultatet skal være et desimaltall?

C ++ – kompilatoren følger ganske enkelt veldefinerte og deterministiske regler som angitt i C ++ – standarden. C ++ – standarden har disse reglene fordi standardkomité bestemte seg for å gjøre det på den måten.

De kunne ha skrevet standarden for å si at heltall matematikk resulterer i flytende tall, eller gjør det bare i tilfellet med en rest. Men det gir kompleksitet: Jeg trenger enten å vite på forhånd hva resultatet er, eller kanskje konvertere tilbake til heltall hvis det alltid gir en flottør. Kanskje jeg vil et heltall .

En av C ++ «s kjernefilosofier er» du betaler ikke for det du ikke bruker . » Hvis du faktisk vil kompleksiteten av å blande heltall og flyt (og de ekstra CPU-instruksjonene og minnetilgang dette medfører 1 ), så gjør typen som du nevnte i spørsmålet ditt . Ellers hold deg til standard heltallsmatematikk.

Til slutt kan blanding av integrerte variabler og flytende punktvariabler føre til tap av presisjon og noen ganger feil resultater som jeg diskuterer nedenfor. Hvis du vil ha dette, så betal for det: ellers tilsier standarden at kompilatorer holder seg til et strengt sett med regler for blanding av datatyper. Dette er veldefinert atferd: Som C ++ -utvikler kan jeg slå opp dette i standarden og se hvordan det fungerer.


Det er i hovedsak tre måter å gjøre det du prøver å gjøre, hver med fordeler og ulemper.

  • Heltalsmatematikk: dette resulterer i avkorting av resultat under divisjon som du fant ut. Hvis du vil ha desimaldelen, må du behandle den separat ved å dele, få resten og behandle desimaldelen som resten delt med deleren. Dette er litt mer komplisert av en operasjon og har flere variabler å sjonglere.

  • Matematikk med flytende punkt: dette vil vanligvis gi riktige (nok) resultater for små verdier, men kan enkelt introdusere feil med presisjon og avrunding, spesielt når eksponenten øker. Hvis du deler et stort tall med et lite tall, kan du til og med føre til en understrømning eller rett og slett få feil resultat fordi skalaene på tallene ikke spiller pent sammen.

  • Gjør din egen matte. Det er klasser der ute som håndterer utvidet presisjon av desimal- og rasjonelle tall . Disse vil vanligvis være tregere enn matte på innebygde typer, men er fremdeles ganske raske og gir matematikk med vilkårlig presisjon. Avrunding og andre problemer er ikke automatiske som med IEEE-flottører, men du får mer kontroll og absolutt mer nøyaktighet.

Nøkkelen her er å velge basert på problemdomenet. . Alle tre metodene for å representere tall har sine egne fordeler og ulemper. Bruker du en loop-teller? Velg en integrert type. Representerer steder i 3D-rom? Sannsynligvis en gruppe flottører. Vil du spore penger? Bruk en fast desimaltype.


1 Mest populære CPU-arkitekturer (f.eks. x86-64 ) vil ha separate sett med instruksjoner som fungerer på forskjellige registertyper, for eksempel heltall og flytpunkt, pluss ekstra instruksjoner for å konvertere mellom integrert, flytende punkt og forskjellige representasjoner av dem (signert og usignert, flyt og dobbelt). Noen av disse operasjonene kan også medføre minnetilgang: konvertere en verdi og lagre den i minnet (dens variabel). Matematikk på CPU-nivå er ikke så enkelt som «heltall i, flyter ut.»Mens det å legge til to heltall kan være en veldig enkel operasjon, muligens en enkelt instruksjon, kan blanding av datatyper øke kompleksiteten.

Kommentarer

  • Du sier at C ++ -standarden foreskriver at oppførselen skal være slik. Hvorfor? Ville ‘ ikke gjøre det lettere å si, » Inndeling av heltall som ikke er jevnt delbare, resulterer i flyt, hvilken som helst annen inndeling er rettferdig spill. »
  • @ moonman239 se mine redigeringer.
  • @ moonman239 Ikke for kompilatorforfattere. Mange ofte brukte CPU-arkitekturer gir et heltalsresultat når de blir bedt om å dele med to heltall. De må implementere en sjekk for resultater som ikke er heltall og deretter bytte til å bruke det langsommere flytpunktet Alternativt kunne de ha misligholdt flytende punktdeling og mistet interessen til de som ønsket rask matematikk, de som ønsket nøyaktig matematikk og de som var vant til C. Endring nå er ikke ‘ t et alternativ fordi det vil bryte kompatibiliteten med eksisterende kode.
  • Ikke at du vil være talsmann for dette som et alternativ, men å lage den statiske typen uttrykk avhenger av kjøretidsverdiene til operandene som er vunnet ‘ for å arbeide med C ++ ‘ s system for statisk type.
  • @ moonman239: Å ha en operasjon som produserer en annen type, avhengig av verdiene til operandene, er ren galskap.

Svar

Dette skyldes utviklingen av maskinvare. Tilbake i de første dagene av datamaskiner hadde ikke alle maskiner en flytende punktenhet, maskinvaren var rett og slett ikke i stand til å forstå forestillingen om et flytende punktnummer. Selvfølgelig kan flytende tallstall implementeres som en abstraksjon av programvare, men det har betydelige ulemper. All aritmetikken på disse maskinene måtte være ren heltalaritmetikk som standard.

Og fremdeles i dag er det et klart skille mellom hel- og flytende aritmetiske enheter i en CPU. Operandene deres er lagret i separate registerfiler til å begynne med, og en heltallsenhet er koblet til å ta to heltallargumenter og produsere et heltalsresultat som ender i et heltallregister. Noen prosessorer krever til og med en heltallverdi som skal lagres i minnet, og lastes deretter tilbake i et flytpunktsregister, før det kan kodes om til et flytpunktsnummer, før du kan utføre en flytpunktsdeling på det.

Som sådan var avgjørelsen som ble tatt av C-utviklerne helt i begynnelsen av språket (C ++ ganske enkelt arvet denne oppførselen), den eneste riktige avgjørelsen å ta, og er fortsatt av verdi i dag: Hvis du trenger flytende matematikk, kan bruke den. Hvis du ikke trenger det, trenger du ikke.

Kommentarer

  • Det er trist at de fleste begrensningene som eksisterte på etableringen av C ++ – standarden er ganske foreldet i dag! For eksempel: » du betaler ikke for det du ikke bruker. » i dag er maskinvare tatt for gitt og alt brukerne ønsker er utførelse!
  • @ mahen23 Ikke alle brukere tenker slik. Jeg jobber i et felt der programmer kjøres på tusenvis av CPU-kjerner parallelt. På dette området er effektivitet penger, både når det gjelder investeringer og når det gjelder rent strømforbruk. Et språk som Java står ikke sjansen i det området, mens C ++ gjør det.
  • @ mahen23 Nei, det er ikke ‘ t – eller bedre, det bare hvis du ser på gjeldende CPU-arkitekturer for skrivebord og eldre. Det er fortsatt mange innebygde systemer som ikke ‘ t eller bare delvis støtter flytende punktoperasjoner, og C så vel som C ++ fortsetter å støtte dem for å gi en mest mulig effektiv implementering kort av ved hjelp av montør. BTW, språk på høyere nivå som Python skiller mellom heltall- og FP-operasjoner – prøv 10 / 3.

Svar

10/2 med heltall gir deg nøyaktig 5 – riktig svar.

Med flytende matematikk kan 10/2 kanskje gi riktig svar *.

Med andre ord er det umulig for flytende tall å være «perfekt» på gjeldende maskinvare – bare heltall matematikk kan være riktig, dessverre kan det ikke gjøre desimaler, men det er lett å jobbe arounds.

For eksempel i stedet for 4/3, gjør (4 * 1000) / (3 * 1000) == 1333. Bare tegn en. i programvare når du viser svaret til brukeren din (1.333). Dette gir deg et nøyaktig svar, i stedet for et som er feil med et antall desimaler.

Matematikkfeil med flytende punkt kan føre til betydelige feil – alt viktig (som økonomi) vil bruke heltallsmatematikk .

* 10/2-eksemplet vil faktisk være riktig med flytende matematikk, men du kan ikke stole på det, mange andre tall gir feil resultater …les mer: http://http.cs.berkeley.edu/~wkahan/ieee754status/ieee754.ps Poenget er at du ikke kan stole på nøyaktighet når flytende punkter er involvert

Kommentarer

  • IEEE 754-kompatible flytpunktsimplementeringer vil gi deg et eksakt resultat for 10 / 2. Faktisk vil de gi deg nøyaktig resultater for enhver operasjon som bare involverer heltalsoperander som har et heltalsresultat forutsatt at operander og resultat kan vises nøyaktig, hvilke «små nok» heltall kan.
  • @ 5gon12eder der ‘ er ikke nødvendig å nit plukke, jeg ‘ Jeg prøver bare å beskrive et komplekst problem i enkle termer. Hele poenget med å støtte ikke-heltallverdier er å ha desimaler ( som kan gjøres ved hjelp av heltall ved ganske enkelt å multiplisere alt med antall desimaler du vil ha som jeg har betalt).

Svar

Selv om det teknisk sett ikke er helt riktig, C ++ regnes fortsatt som et supersett av C, ble inspirert av det og som sådan tilegnet seg noen av egenskapene, heltall er en av dem.

C var for det meste designet for å være effektiv og rask, og heltall er generelt mye raskere enn flytende poeng, fordi heltallstypen er knyttet til maskinvare, mens flytende punkter må beregnes.

Når / operand mottar to heltall, ett på venstre side og en til høyre, kan det ikke en gang gjøre inndeling i det hele tatt, resultatet kan beregnes ved hjelp av enkelt tillegg og en løkke, og spør hvor mange ganger passer operanden på høyre side inn i operanden til venstre.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *