Miért eredményez egész szám osztást?

A Bevezetés a programozásba megtudtuk, hogy ha két egész számot osztasz, akkor mindig kapsz egész számot. A probléma megoldásához állítson ezekből az egészek közül legalább egyet lebegővé.

Miért nem érti a fordító, hogy azt akarom, hogy az eredmény tizedes legyen?

Megjegyzések

  • Talán azt szeretné, hogy az eredmény egész szám legyen. Hogyan különbözteti meg a különbség?
  • Mivel a C ++ szabvány ezt mondja.
  • @soandos: Ahogy Pascal és Python is csinálja: van egy különálló egész osztás operátoruk, de a standard osztás operátor mindig a matematikailag helyes eredményt adja vissza. (Vagy – a pedáns számára – olyan helyes, amennyire csak lehet korlátai az FP matematikának.)
  • Honnan tudná a fordító, hogy az eredmény tizedes szám legyen? Megalapozott okok vannak egy egész számra.
  • @soandos Pythonban // az egész-osztás operátor, a # egysoros megjegyzés. A Pascal-ban az egész-osztás operátor a div. Mindkettő elég jól működik a megfelelő nyelvükhöz ges. A C valószínűleg a lehető legrosszabbat teszi: egy operátor, amely tetszőleges kontextus alapján két teljesen különböző dolgot képes megtenni.

Válasz

Miért nem érti a fordító, hogy azt akarom, hogy az eredmény tizedes legyen?

A C ++ fordító egyszerűen követi a jól definiált és determinisztikus szabályokat a C ++ szabványban meghatározottak szerint. A C ++ szabványnak vannak ilyen szabályai, mert a szabványbizottság úgy döntött, hogy így készül.

Megírhatták volna a szabványt, mondván, hogy az egész matematika lebegőpontos számokat eredményez, vagy csak A maradék esete. Ez azonban bonyolultabbá teszi: Vagy idő előtt tudnom kell, mi az eredmény, vagy esetleg vissza kell alakítanom egész számra, ha mindig úszt ad. Lehet, hogy szeretnék egy egész számot .

A C ++ egyik alapfilozófiája az, hogy “nem fizet azért, amit nem használ . ” Ha valóban szeretné az egész számok és az úszók keverésének összetettségét (és ez az extra CPU-utasításokhoz és a memóriahozzáférésekhez 1 -et von maga után), akkor tegye a típusát úgy, ahogy azt a kérdésében említette . Ellenkező esetben ragaszkodjon a szokásos egész szám matematikához.

Végül az integrált és lebegőpontos változók keverése a pontosság elvesztéséhez és néha helytelen eredményekhez vezethet, amint azt az alábbiakban tárgyalom. Ha ezt akarja, akkor fizessen érte: ellenkező esetben a szabvány előírja, hogy a fordítók szigorú szabályrendszerhez ragaszkodjanak az adattípusok keverésére. Ez jól definiált viselkedés: C ++ fejlesztőként ezt megnézhetem a szabványban, és megnézhetem, hogyan működik.


Alapvetően háromféleképpen lehet megtenni azt, amit próbál, mindegyik előnyökkel és hátrányokkal rendelkezik.

  • Egész matematika: ennek eredményeként csonka eredmények születnek az osztás során, ahogy megtudta. Ha a tizedes részt akarja, akkor ezt külön kell kezelnie, elosztva, megkapva a maradékot, és a tizedes részt úgy, hogy a maradékot elosztják az osztóval. Ez egy művelet egy kicsit bonyolultabb, és több változóval lehet zsonglőrködni.

  • Lebegőpontos matematika: ez általában helyes (elég) eredményt ad kis értékekhez, de képes könnyen behozhatja a hibákat pontossággal és kerekítéssel, különösen az exponens növekedésével. Ha nagy számot osztasz meg kis számmal, akkor akár alulcsordulást is okozhat, vagy egyszerűen rossz eredményt érhet el, mert a számok skálája nem játszik jól egymással.

  • Végezzen saját matekot. Vannak osztályok, amelyek tizedes és racionális számok kiterjesztett pontosságát kezelik . Ezek általában lassabbak, mint a matematika a beépített típusoknál, de általában még mindig elég gyorsak és tetszőleges pontosságú matematikát nyújtanak. A kerekítés és más kérdések nem automatikusak, mint az IEEE úszóknál, de nagyobb irányítást és minden bizonnyal nagyobb pontosságot nyer.

Itt a legfontosabb a problémás tartomány alapján választani . A számok ábrázolásának mindhárom módszerének megvannak a maga előnyei és hátrányai. Használ egy hurokszámlálót? Válasszon integrált típust. Helyeket ábrázol a 3D térben? Valószínűleg úszók egy csoportja. Szeretné nyomon követni a pénzt? Használjon fix decimális típust.


1 A legnépszerűbb CPU-architektúrák (pl. x86-64 ) külön utasításkészletekkel fog rendelkezni, amelyek különféle regisztertípusokon, például egész számon és lebegőponton működnek, valamint további utasításokat az integrál, lebegőpont és ezek különböző ábrázolásai (aláírt és aláíratlan, lebegő és kettős) közötti átalakításhoz. Ezen műveletek némelyike memória-hozzáféréssel is járhat: konvertáljon egy értéket és tárolja azt a memóriába (annak változója). A matematika a CPU szintjén nem olyan egyszerű, mint az “egész szám, lebegés”.”Bár két egész szám hozzáadása nagyon egyszerű művelet, esetleg egyetlen utasítás, az adattípusok keverése növelheti a bonyolultságot.

Megjegyzések

  • Ön azt mondja, hogy a C ++ szabvány előírja, hogy ennek a viselkedésnek így kell lennie. Miért? Nem ‘ nem könnyítené meg a dolgok kimondását, ” Azok az egész számok felosztása, amelyek nem egyenletesen oszthatók, úszóként eredményeznek, minden más felosztás tisztességes játék. ”
  • @ moonman239 lásd a szerkesztéseimet.
  • @ moonman239 Nem fordítói írók számára. Számos általánosan használt CPU-architektúra egész eredményt ad, ha két egész számmal történő osztást kérnek tőlük. Ellenőrizniük kell a nem egész szám eredményeit, majd át kell váltaniuk a lassabb lebegőpont használatára Alternatív megoldásként elmozdíthatták volna a lebegőpontos osztást, és elvesztették azok érdeklődését, akik gyors matematikát akartak, akik pontos matematikát akartak, és akik megszokták, hogy C. nem ‘ t egy opció, mert ez megszakítaná a kompatibilitást a meglévő kóddal.
  • Nem mintha ezt alternatívaként támogatnád, hanem statikus típusú kifejezést csinálnál attól függenek, hogy az operandusok futási idejű értékei ‘ t nem működnek-e a C ++ ‘ s statikus típusú rendszerrel.
  • @ moonman239: Egy olyan művelet végrehajtása, amely az operandusok értékei től függően eltérő típust eredményez, puszta őrültség.

Válasz

Ennek oka a hardver fejlődése. A számítógépek korai szakaszában nem minden gépben volt lebegőpontos egység, a hardver egyszerűen nem volt képes megérteni a lebegőpontos szám fogalmát. Természetesen a lebegőpontos számok szoftver absztrakcióként is megvalósíthatók, de ennek jelentős hátrányai vannak. Ezeknek a gépeknek az összes aritmetikájának alapértelmezés szerint tiszta egész számtannak kell lennie.

És még ma is szilárd különbség van az egész és a lebegőpontos aritmetikai egységek között a CPU-n belül. Az operandusaik eleve külön regiszterfájlokban vannak tárolva, és egy egész egységet arra hívnak fel, hogy két egész argumentumot vegyen és egy egész eredményt hozzon létre, amely egy egész regiszterbe kerül. Egyes CPU-k megkövetelik még egy egész érték tárolását a memóriában, majd újratöltését egy lebegőpontos regiszterbe, mielőtt át lehet kódolni lebegőpontos számra, mielőtt lebegőpontos osztást hajthatna végre rajta.

Mint ilyen, a C fejlesztők által a nyelv legelején meghozott döntés (a C ++ egyszerűen örökölte ezt a viselkedést) volt az egyetlen megfelelő döntés, amely ma is értékes: Ha lebegőpontos matematikára van szüksége, akkor használhatja. Ha nincs rá szüksége, akkor nem is kell.

Megjegyzések

  • Szomorú, hogy a legtöbb kényszer a a C ++ szabvány létrehozása ma már eléggé elavult! Például: ” nem fizet azért, amit nem használ. ” manapság a hardvert magától értetődőnek tekintik, és minden felhasználó kéri, hogy: végrehajtás!
  • @ mahen23 Nem minden felhasználó gondolkodik így. Olyan területen dolgozom, ahol a programokat több ezer CPU-magon futtatják párhuzamosan. Ezen a területen a hatékonyság pénz, mind a beruházások, mind a puszta energiafogyasztás szempontjából. Egy olyan nyelv, mint a Java, nem állja meg a véletlen szellemét ezen a területen, míg a C ++.
  • @ mahen23 Nem, ez nem ‘ t – vagy még jobb, ez csak akkor tekinthető meg, ha az asztali számítógépek és a fentiek jelenlegi CPU-architektúráját nézzük. Még mindig sok olyan beágyazott rendszer létezik, amelyek nem ‘ t, vagy csak részben támogatják a lebegőpontos műveleteket, és a C, valamint a C ++ továbbra is támogatja őket a lehető leghatékonyabb megvalósítás érdekében. assembler segítségével. BTW, még a magasabb szintű nyelvek, mint a Python, megkülönböztetik az egész és az FP műveleteket – próbáld ki a 10 / 3.

Válasz

A 10/2 egész számokkal pontosan 5-öt ad – a helyes választ.

Lebegőpontos matematika esetén a 10/2 lehet a helyes válasz *.

Más szavakkal, lehetetlen, hogy a lebegőpontos számok “tökéletesek” legyenek a jelenlegi hardveren – csak az egész matematika lehet helyes, sajnos nem képes tizedesjegyekre, de könnyű a munka körül.

Például a 4/3 helyett tegye a következőt: (4 * 1000) / (3 * 1000) == 1333. Csak rajzoljon egy a-t a szoftverbe, amikor megjeleníti a választ a felhasználójának (1.333). Ez pontos választ ad, ahelyett, hogy bizonyos tizedesjegyekkel téves lenne.

A lebegőpontos matematikai hibák összeadódva jelentős hibákat okozhatnak – bármi fontos (például pénzügy) egész számot használ .

* a 10/2-es példa valóban helyes lesz lebegőpontos matematikával, de nem lehet rá hagyatkozni, sok más szám helytelen eredményt ad …további részletekért olvassa el: http://http.cs.berkeley.edu/~wkahan/ieee754status/ieee754.ps A lényeg az, hogy nem lehet megbízni a pontosságban, amikor lebegőpontok vannak érintett

megjegyzések

  • Az IEEE 754 kompatibilis lebegőpontos implementációk pontos eredményt adnak a 10/2-re. Valójában pontosan megadják minden olyan művelet eredményei, amelyek csak egész operandumokat tartalmaznak, amelyeknek egész eredménye van, feltéve, hogy az operandusok és az eredmények pontosan ábrázolhatók, amelyek az „elég kicsi” egész számok képesek lenni. / div> s nem kell nit választani, én ‘ csak egy komplex problémát próbálok egyszerű leírással leírni. A nem egész értékek támogatásának teljes lényege, hogy tizedesjegyek legyenek ( ami egész számok felhasználásával elvégezhető úgy, hogy mindent egyszerűen megszorzunk a kívánt tizedesjegyek számával, ahogy én félretettem).

Válasz

Bár technikailag nem teljesen helyes, A C ++ még mindig a C szuperhalmazának számít, ihlette ezt, és mint ilyen, tulajdonított néhány tulajdonságát, az egész szám felosztása az egyikük.

A C-t többnyire hatékonynak és gyorsnak tervezték, és az egészek általában sokkal gyorsabban, mint a lebegőpontok, mert az egész típusú hardverhez van kötve, míg a lebegőpontokat ki kell számolni.

Amikor a / operandus két egész számot kap, egyet a bal és a jobb oldali résznél egyáltalán nem is végezhet osztást, az eredményt egyszerű összeadás és egy hurok segítségével lehet kiszámítani, megkérdezve, hogy a jobb oldali operandus hányszor illeszkedik a bal oldali operandusba.

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük