Hvorfor resulterer heltalsdeling i et heltal?

Vi lærte i introduktion til programmering, at hvis du deler to heltal, får du altid et heltal. For at løse problemet skal du mindst et af disse heltal flyde.

Hvorfor forstår ikke kompilatoren, at resultatet skal være et decimaltal?

Kommentarer

  • Måske vil du have, at resultatet bliver et heltal i stedet. Hvordan kan det fortælle forskellen?
  • Fordi C ++ -standarden siger det.
  • @soandos: Den måde, som Pascal og Python begge gør det på: har en særskilt heltalsdivisionsoperator, men standarddivisionsoperatøren returnerer altid det matematisk korrekte resultat. (Eller – for pedant – så korrekt som du kan få givet begrænsninger for FP-matematik.)
  • Hvordan ville compileren vide, at du vil have, at resultatet skal være et decimaltal? Der er gyldige grunde til at have et heltal.
  • @soandos I Python, // er heltalsopdelingsoperatoren og # er en enkeltlinjekommentar. I Pascal er heltalsopdelingsoperator nøgleordet div. Begge fungerer ret godt for deres respektive sprog ges. C gør sandsynligvis den værste mulige ting: en operatør, der kan gøre to helt forskellige ting baseret på vilkårlig sammenhæng.

Svar

Hvorfor forstår ikke kompilatoren, at resultatet skal være et decimaltal?

C ++ – kompilatoren følger ganske enkelt veldefinerede og deterministiske regler som beskrevet i C ++ – standarden. C ++ – standarden har disse regler, fordi standardudvalg besluttede at gøre det på den måde.

De kunne have skrevet standarden for at sige, at heltal matematik resulterer i flydende tal eller kun gør det i tilfældet med en rest. Men det tilføjer kompleksitet: Jeg skal enten vide på forhånd, hvad resultatet er, eller måske konvertere tilbage til heltal, hvis det altid giver en float. Måske vil jeg have et heltal .

En af C ++ s kernefilosofier er “du betaler ikke for det, du ikke bruger . ” Hvis du faktisk ønsker kompleksiteten ved at blande heltal og floats (og de ekstra CPU-instruktioner og hukommelsesadgang dette medfører 1 ), skal du udføre den type cast, som du nævnte i dit spørgsmål . Ellers skal du holde dig til standard heltalsmatematik.

Endelig kan blanding af integrerede og flydende punktvariabler resultere i tab af præcision og undertiden forkerte resultater, som jeg diskuterer nedenfor. Hvis du vil have dette, skal du betale for det: ellers dikterer standarden, at kompilatorer holder sig til et strengt sæt regler for blanding af datatyper. Dette er veldefineret adfærd: Som C ++ -udvikler kan jeg slå dette op i standarden og se, hvordan det fungerer.


Der er i det væsentlige tre måder at gøre, hvad du prøver at gøre, hver med fordele og ulemper.

  • Heltals matematik: dette resulterer i afkortning af resultater under division, som du fandt ud af. Hvis du vil have decimaldelen, skal du behandle den separat ved at dividere, få resten og behandle decimaldelen som resten divideret med deleren. Dette er en smule mere kompleks af en operation og har flere variabler at jonglere.

  • Flydende matematik: dette vil generelt give korrekte (nok) resultater til små værdier, men kan let introducere fejl med præcision og afrunding, især når eksponenten øges. Hvis du deler et stort tal med et lille antal, kan du endda forårsage en understrømning eller blot få et forkert resultat, fordi skalaerne på tallene ikke spiller pænt sammen.

  • Lav din egen matematik. Der er klasser derude, der håndterer udvidet præcision med decimaler og rationelle tal . Disse vil typisk være langsommere end matematik på indbyggede typer, men er generelt stadig ret hurtige og giver matematisk præcision matematik. Afrunding og andre problemer er ikke automatiske, som de er med IEEE-floats, men du får mere kontrol og bestemt mere nøjagtighed.

Nøglen her er at vælge ud fra problemdomænet. . Alle tre metoder til at repræsentere tal har deres egne fordele og ulemper. Brug en loop-tæller? Vælg en integreret type. Repræsenterer placeringer i 3D-rum? Sandsynligvis en gruppe flyde. Vil du spore penge? Brug en bestemt decimal type.


1 Mest populære CPU-arkitekturer (f.eks. x86-64 ) vil have separate sæt instruktioner, der fungerer på forskellige registertyper såsom heltal og flydende punkt, plus ekstra instruktioner til at konvertere mellem integreret, flydende punkt og forskellige repræsentationer af dem (underskrevet og usigneret, flyde og dobbelt). Nogle af disse operationer kan også medføre hukommelsesadgang: konvertere en værdi og gemme den i hukommelsen (dens variabel). Matematik på CPU-niveau er ikke så simpelt som “heltal i, flyder ud.”Mens tilføjelse af to heltal kan være en meget enkel handling, muligvis en enkelt instruktion, kan blanding af datatyper øge kompleksiteten.

Kommentarer

  • Du siger, at C ++ -standarden foreskriver, at denne adfærd skal være sådan. Hvorfor? Ville ‘ det ikke gøre tingene lettere at sige, ” Opdeling af heltal, der ikke er jævnt delbare, resulterer i floats, enhver anden division er fair game. ”
  • @ moonman239 se mine redigeringer.
  • @ moonman239 Ikke til kompilatorforfattere. Mange almindeligt anvendte CPU-arkitekturer giver et heltalsresultat, når de bliver bedt om at opdele med to heltal. De bliver nødt til at gennemføre en kontrol af ikke-heltal resultater og derefter skifte til at bruge det langsommere flydende punkt Alternativt kunne de have misligholdt floating point division og mistet interessen for dem, der ønskede hurtig matematik, dem, der ønskede nøjagtig matematik, og dem, der var vant til C. Ændring nu er ikke ‘ t en mulighed, fordi det ville bryde kompatibilitet med eksisterende kode.
  • Ikke at du vil anbefale dette som et alternativ, men at lave den statiske type af et udtryk afhænger af kørselsværdierne for de operander, der er vundet ‘ for at arbejde med C ++ ‘ s statiske system.
  • @ moonman239: At have en operation, der producerer en anden type afhængigt af værdierne af operanderne, er ren galskab.

Svar

Dette skyldes udviklingen af hardware. Tilbage i de tidlige dage af computere havde ikke alle maskiner en flydende enhed, hardware var simpelthen ikke i stand til at forstå forestillingen om et flydende nummer. Selvfølgelig kan flydende numre implementeres som en software-abstraktion, men det har betydelige ulemper. Alt aritmetik på disse maskiner skulle være rent heltal aritmetik som standard.

Og stadig i dag er der en klar skelnen mellem heltal og flydende aritmetiske enheder i en CPU. Deres operander gemmes i separate registerfiler til at begynde med, og en heltalsenhed er kablet til at tage to heltalargumenter og producere et heltalresultat, der ender i et heltalregister. Nogle CPUer kræver endda et heltal, der skal lagres i hukommelsen, og derefter genindlæses tilbage i et flydende register, før det kan genkodes til et flydende nummer, før du kan udføre en flydende punktopdeling på det.

Som sådan var beslutningen truffet af C-udviklerne helt i starten af sproget (C ++ arvede simpelthen denne adfærd), den eneste passende beslutning at tage og er stadig af værdi i dag: Hvis du har brug for flydende matematik, er du kan bruge det. Hvis du ikke har brug for det, behøver du ikke.

Kommentarer

  • Det er trist, at de fleste af de begrænsninger, der eksisterede på oprettelsen af C ++ – standarden er ret forældet i dag! For eksempel: ” du betaler ikke for det, du ikke bruger. ” i dag tages hardware for givet, og alt, hvad brugerne ønsker, er udførelse!
  • @ mahen23 Ikke alle brugere tænker sådan. Jeg arbejder i et felt, hvor programmer køres parallelt på tusinder af CPU-kerner. På dette område er effektivitet penge, både med hensyn til investeringer og med hensyn til rent strømforbrug. Et sprog som Java står ikke spøgelsen for en chance i dette område, mens C ++ gør det.
  • @ mahen23 Nej, det er ikke ‘ t – eller bedre, det kun hvis du ser på nuværende CPU-arkitekturer til desktops og derover. Der er stadig mange indlejrede systemer, der ikke ‘ t eller kun delvis understøtter flydende punktoperationer, og C såvel som C ++ fortsætter med at understøtte dem for at give den mest effektive implementering uden for ved hjælp af samler. BTW, sprog på endnu højere niveau som Python skelner mellem heltal og FP-operationer – prøv 10 / 3.

Svar

10/2 med heltal giver dig nøjagtigt 5 – det rigtige svar.

Med flydende matematik giver 10/2 måske det rigtige svar *.

Med andre ord er det umuligt for flydende tal at være “perfekt” på nuværende hardware – kun heltal matematik kan være korrekt, desværre kan det ikke gøre decimaler, men der er let arbejde arounds.

For eksempel i stedet for 4/3, skal du gøre (4 * 1000) / (3 * 1000) == 1333. Tegn bare en. i software, når du viser svaret til din bruger (1.333). Dette giver dig et nøjagtigt svar i stedet for et, der er forkert med et antal decimaler.

Matematikfejl i flydende punkt kan tilføjes til at forårsage betydelige fejl – alt vigtigt (som økonomi) bruger heltal matematik .

* 10/2-eksemplet vil faktisk være korrekt med flydende matematik, men du kan ikke stole på det, mange andre tal giver forkerte resultater …læs mere for at få flere detaljer: http://http.cs.berkeley.edu/~wkahan/ieee754status/ieee754.ps Pointen er, at du ikke kan stole på nøjagtighed, når der er flydende punkter involveret

Kommentarer

  • IEEE 754-kompatible floating-point-implementeringer giver dig et nøjagtigt resultat for 10 / 2. Faktisk giver de dig nøjagtige resultater for enhver operation, der kun involverer heltalsoperander, der har et heltalsresultat, forudsat at operander og resultat kan repræsenteres nøjagtigt, hvilke “små nok” heltal kan.
  • @ 5gon12eder der ‘ er ikke nødvendigt at nit pick, jeg ‘ Jeg prøver bare at beskrive et komplekst problem i enkle termer. Hele punktet med at understøtte ikke-heltal værdier er at have decimaler ( hvilket kan gøres ved hjælp af heltal ved simpelthen at multiplicere alt med antallet af decimaler, som du ønsker, som jeg har trukket).

Svar

Selvom det teknisk set ikke er helt korrekt, C ++ betragtes stadig som et supersæt af C, blev inspireret af det og tilegnede som sådan nogle af dets egenskaber, da heltal er en af dem.

C var for det meste designet til at være effektiv og hurtig, og heltal er generelt meget hurtigere end flydende punkter, fordi heltalstypen er bundet til hardware, mens flydende punkter skal beregnes.

Når / operand modtager to heltal, et på venstre side og en til højre, kan det ikke engang opdele overhovedet, resultatet kan beregnes ved hjælp af simpel tilføjelse og en løkke, hvor man spørger, hvor mange gange passer operanden på højre side ind i operanden til venstre.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *