Miksi kokonaislukujako johtaa kokonaislukuun?

Opimme ohjelmoinnin johdannossa, että jos jaat kaksi kokonaislukua, saat aina kokonaisluvun. Korjaa ongelma tekemällä ainakin yksi näistä kokonaisluvuista kelluva.

Miksi kääntäjä ei ymmärrä, että haluan tuloksen olevan desimaaliluku?

Kommentit

  • Ehkä haluat, että tulos on kokonaisluku. Kuinka se voi erottaa toisistaan?
  • Koska C ++ -standardi sanoo niin.
  • @soandos: Tapa, jolla Pascal ja Python molemmat tekevät sen: niillä on erillinen kokonaislukujako-operaattori, mutta vakio-jako-operaattori palauttaa aina matemaattisesti oikean tuloksen. (Tai – pedanttiseksi – niin oikeaksi kuin saat FP-matematiikan rajoitukset.)
  • Mistä kääntäjä tietäisi, että haluat tuloksen olevan desimaaliluku? On olemassa perusteltuja syitä kokonaisluvulle.
  • @soandos Pythonissa // on kokonaislukujako-operaattori ja # on yksirivinen kommentti. Pascalissa kokonaislukujako-operaattori on avainsana div. Molemmat toimivat melko hyvin omalla kielellään ges. C tekee todennäköisesti pahimman mahdollisen: yksi operaattori, joka voi tehdä kaksi täysin erilaista asiaa mielivaltaisen kontekstin perusteella.

Vastaa

Miksi kääntäjä ei ymmärrä, että haluan tuloksen olevan desimaaliluku?

C ++-kääntäjä noudattaa yksinkertaisesti tarkasti määriteltyjä ja deterministisiä sääntöjä C ++ -standardin mukaisesti. C ++ -standardilla on nämä säännöt, koska standardikomitea päätti tehdä sen tuolla tavalla.

He olisivat voineet kirjoittaa standardin sanomaan, että kokonaisluku matematiikka johtaa liukulukuihin tai tekee sen vain Tämä lisää kuitenkin monimutkaisuutta: minun on joko tiedettävä etukäteen tulos, tai voin muuntaa takaisin kokonaislukuksi, jos se antaa aina kelluvan. Ehkä haluan kokonaisluvun .

Yksi C ++: n perusfilosofioista on ”et maksa siitä, mitä et käytä ” Jos todella haluat kokonaislukujen ja kellukkeiden sekoittamisen monimutkaisuuden (ja tähän liittyvät ylimääräiset suorittimen ohjeet ja muistihakemukset edellyttävät 1 ), tee tyyppivalu kuten mainitsit kysymyksessäsi . Muussa tapauksessa pidä kiinni tavallisesta kokonaislukumatematiikasta.

Lopuksi integraalisten ja liukulukuisten muuttujien sekoittaminen voi johtaa tarkkuuden menetykseen ja joskus virheellisiin tuloksiin, kuten keskustelen alla. Jos haluat tämän, maksa sitten siitä: muuten standardi määrää, että kääntäjät noudattavat tiukkoja sääntöjä tietotyyppien sekoittamiseksi. Tämä on hyvin määritelty käyttäytyminen: C ++ -kehittäjänä voin etsiä tämän standardista ja nähdä, miten se toimii.


Yrität tehdä periaatteessa kolmella tavalla, jokaisella on etuja ja haittoja.

  • Kokonaisluku: tämä johtaa tuloksen katkaisemiseen jakamisen aikana, kuten huomasit. Jos haluat desimaaliosan, sinun on käsiteltävä sitä erikseen jakamalla, hankkimalla loppuosa ja käsittelemällä desimaaliosa loppuosana jaettuna jakajalla. Tämä on hieman monimutkaisempi operaatio ja siinä on enemmän muuttujia, joilla voi taistella.

  • Liukuluku matematiikka: tämä tuottaa yleensä oikeita (tarpeeksi) tuloksia pienille arvoille, mutta voi aiheuttaa virheitä helposti tarkasti ja pyöristämällä, varsinkin kun eksponentti kasvaa. Jos jaat suuren määrän pienellä luvulla, saatat jopa aiheuttaa alivirran tai yksinkertaisesti saada väärän tuloksen, koska numeroiden asteikot eivät pelaa hienosti keskenään.

  • Tee oma matematiikka. On luokkia, jotka käsittelevät desimaalilukujen ja rationaalilukujen laajennettua tarkkuutta . Nämä ovat tyypillisesti hitaampia kuin matematiikka sisäänrakennetuilla tyypeillä, mutta ovat yleensä silti melko nopeita ja tarjoavat mielivaltaisen tarkkaa matematiikkaa. Pyöristäminen ja muut ongelmat eivät ole automaattisia, kuten IEEE-kellukkeiden kohdalla, mutta saat enemmän hallintaa ja varmasti enemmän tarkkuutta.

Tärkeintä tässä on valita ongelma-alueen perusteella . Kaikilla kolmella numeroiden esittämistavalla on omat edut ja haitat. Käytätkö silmukkalaskuria? Valitse integraalityyppi. Edustatko sijainteja 3D-tilassa? Todennäköisesti joukko kellukkeita. Haluatko seurata rahaa? Käytä kiinteän desimaalin tyyppiä.


1 Suosituimmat suorittimen arkkitehtuurit (esim. x86-64 ) on erilliset ohjeet, jotka toimivat erilaisilla rekisterityypeillä, kuten kokonaisluku ja liukuluku, sekä ylimääräiset ohjeet muunnettavaksi integraalin, liukuluvun ja niiden eri esitysten välillä (allekirjoitetut ja allekirjoittamattomat, kelluvat ja kaksinkertaiset). Jotkut näistä toiminnoista saattavat edellyttää myös muistin käyttöä: muunna arvo ja tallenna se muistiin (sen muuttuja). Matematiikka suorittimen tasolla ei ole niin yksinkertaista kuin ”kokonaisluku sisään, kellua ulos.”Vaikka kahden kokonaisluvun lisääminen voi olla hyvin yksinkertainen operaatio, mahdollisesti yksi käsky, tietotyyppien sekoittaminen voi lisätä monimutkaisuutta.

Kommentit

  • Sanot, että C ++ -standardissa määrätään, että tällaisen käyttäytymisen on oltava niin. Miksi? Eikö se tekisi asioita helpommaksi sanoa, ”

/ div> Niiden kokonaislukujen jakaminen, jotka eivät ole tasan jaettavissa, johtaa kellukkeisiin, mikä tahansa muu jako on reilu peli. ”

  • @ moonman239 katso muokkaukseni.
  • @ moonman239 Ei kääntäjien kirjoittajille. Monet yleisesti käytetyt CPU-arkkitehtuurit tuottavat kokonaislukutuloksen, kun niitä pyydetään jakamaan kaksi kokonaislukua. Heidän on suoritettava tarkistus ei-kokonaislukujen tuloksista ja vaihdettava sitten käyttämään hitaampaa kelluvaa pistettä Vaihtoehtoisesti he olisivat voineet jättää vaihtamatta liukulukujakoa ja menettää kiinnostuksen niille, jotka haluavat nopeaa matematiikkaa, niille, jotka haluavat tarkkaa matematiikkaa, ja niille, jotka ovat tottuneet C: hen. ole ’ t vaihtoehto, koska se rikkoo yhteensopivuuden olemassa olevan koodin kanssa.
  • Ei, ettet kannattaisi tätä vaihtoehtona, mutta tekisit staattisen lauseketyypin riippuvat operandien ajonaikaisista arvoista, jotka ’ eivät toimi C ++ ’ staattisen tyyppisen järjestelmän kanssa.
  • @ moonman239: Jos operaatio tuottaa eri tyypin operandien arvoista riippuen, se on pelkkää hulluutta.
  • Vastaa

    Tämä johtuu laitteistokehityksestä. Tietokoneiden alkuaikoina kaikilla koneilla ei ollut liukulukuyksikköä, laitteisto ei yksinkertaisesti pystynyt ymmärtämään liukulukun käsitystä. Tietysti liukuluvut voidaan toteuttaa ohjelmiston abstraktiona, mutta sillä on merkittäviä haittoja. Näiden koneiden kaikkien aritmeettisten ominaisuuksien oli oletusarvoisesti oltava puhtaan kokonaisluvun aritmeettisia.

    Ja vielä nykyäänkin prosessorin sisällä on luja ero kokonaisluku- ja liukulukuisten aritmeettisten yksiköiden välillä. Niiden operandit tallennetaan aluksi erillisiksi rekisteritiedostoiksi, ja kokonaislukuyksikkö johdetaan ottamaan kaksi kokonaislukuargumenttia ja tuottamaan kokonaislukutulos, joka päätyy kokonaislukurekisteriin. Jotkut suorittimet vaativat jopa kokonaisluvun tallentamisen muistiin ja sitten uudelleen ladattavan liukulaskurekisteriin, ennen kuin se voidaan koodata liukulukuiksi, ennen kuin voit suorittaa sille liukulukujaon.

    Sellaisena C-kehittäjien tekemä päätös kielen alussa (C ++ yksinkertaisesti peri tämän käyttäytymisen), oli ainoa sopiva päätös, ja se on edelleen arvokas tänään: Jos tarvitset liukulaskematematiikkaa, voi käyttää sitä. Jos et tarvitse sitä, niin sinun ei myöskään tarvitse.

    Kommentit

    • On surullista, että suurin osa C ++ -standardin luominen on tänään melko vanhentunutta! Esimerkiksi: ” et maksa siitä, mitä et käytä. ” nykyään laitteisto pidetään itsestäänselvyytenä ja kaikki käyttäjät haluavat toteutus!
    • @ mahen23 Kaikki käyttäjät eivät ajattele näin. Työskentelen kentällä, jossa ohjelmia ajetaan samanaikaisesti tuhansilla suorittimen ytimillä. Tällä alalla tehokkuus on rahaa sekä investointien että pelkän virrankulutuksen suhteen. Java-kaltainen kieli ei salli mahdollisuutta tällä alueella, kun taas C ++.
    • @ mahen23 Ei, se ei ole ’ t – tai parempi, se vain, jos tarkastelet nykyisiä työasemien ja uudempien CPU-arkkitehtuureja. On edelleen monia sulautettuja järjestelmiä, jotka eivät

    t tai vain osittain tukevat liukulukuoperaatioita, ja C sekä C ++ tukevat niitä edelleen, jotta mahdollistetaan mahdollisimman tehokas toteutus. käyttämällä kokoonpanijaa. BTW, jopa korkeamman tason kielet, kuten Python, erottavat kokonaisluku- ja FP-toiminnot – kokeile10 / 3.

    Vastaa

    10/2 kokonaisluvuilla antaa tarkalleen 5 – oikean vastauksen.

    Liukulaskematematiikalla 10/2 saattaa antaa oikean vastaus *.

    Toisin sanoen on mahdotonta, että liukuluvut ovat ”täydellisiä” nykyisessä laitteistossa – vain kokonaisluku matematiikka voi olla oikea, valitettavasti se ei voi tehdä desimaaleja, mutta on helppoa työtä arounds.

    Esimerkiksi 4/3: n sijaan tee (4 * 1000) / (3 * 1000) == 1333. Piirrä vain a. ohjelmistossa, kun näytät vastauksen käyttäjälle (1.333). Tämä antaa sinulle tarkan vastauksen sen sijaan, että se olisi virheellinen tietyllä desimaalilla.

    Vaihteluverkon matemaattiset virheet voivat lisätä merkittäviä virheitä – kaikki tärkeät (kuten talous) käyttävät kokonaislukumatematiikkaa. .

    * 10/2-esimerkki on oikeastaan oikea liukulaskematematiikassa, mutta siihen ei voi luottaa, monet muut numerot antavat vääriä tuloksia …lisätietoja lukemalla: http://http.cs.berkeley.edu/~wkahan/ieee754status/ieee754.ps Tarkoituksena on, ettet voi luottaa tarkkuuteen aina, kun liukupisteet ovat mukana

    kommentit

    • IEEE 754 -yhteensopivien liukulukujen toteutus antaa sinulle tarkan tuloksen arvosta 10/2. Itse asiassa ne antavat sinulle tarkan tulokset kaikista operaatioista, jotka sisältävät vain kokonaislukuoperandeja, joilla on kokonaisluku, edellyttäen, että operandit ja tulos voidaan esittää tarkalleen, mikä ”riittävän pieni” kokonaisluku voi olla.
    • @ 5gon12eder siellä ’ s ei tarvitse nittaa valintaa, minä ’ yritin vain kuvata monimutkaista ongelmaa yksinkertaisella tavalla. Koko ei-kokonaislukuarvojen tukemisessa on oltava desimaalit ( joka voidaan tehdä käyttämällä kokonaislukuja kertomalla kaikki yksinkertaisesti haluamallasi desimaalilukumäärällä.

    Vastaa

    Vaikka teknisesti se ei olekaan oikein, C ++: ta pidetään edelleen C: n yläjoukkona, se on sen innoittama ja sellaisenaan omistanut joitain sen ominaisuuksia, kokonaislukujako on yksi niistä.

    C suunniteltiin enimmäkseen tehokkaaksi ja nopeaksi, ja kokonaisluvut ovat yleensä paljon nopeammin kuin liukuluvut, koska kokonaislukutyyppi on sidottu laitteistoon, kun taas liukuluvut on laskettava.

    Kun operandi / saa kaksi kokonaislukua, yksi vasemmalla ja oikealla puolella, se ei välttämättä edes jaa lainkaan, tulos voidaan laskea käyttämällä yksinkertaista lisäystä ja silmukkaa, kysyen kuinka monta kertaa oikean puolen operandi mahtuu vasemmanpuoleiseen operandiin.

    Vastaa

    Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *