Tizedes és egész számok kerekítése Pythonban a “round” és a “Decimal.quantize” segítségével

Üzleti

A következőkben azt mutatjuk be, hogyan kerekíthetünk számokat Pythonban kerekítéssel vagy páros számra kerekítéssel. A számokról feltételezzük, hogy lebegőpontos float vagy integer int típusúak.

  • beépített függvény (pl. programozási nyelvben): round()
    • A tizedesjegyeket tetszőleges számú számjegyre kerekítheti.
    • Egész számok kerekítése tetszőleges számú számjegyre.
    • round() páros számra kerekít, nem pedig egy közös kerekítésre.
  • szabványos könyvtárdecimal quantize()
    • DecimalObjektum létrehozása
    • Tizedesjegyek kerekítése tetszőleges számjegyekre és kerekítés páros számokra
    • Egész számok kerekítése tetszőleges számú számjegyre és kerekítés páros számokra
  • Új függvény definiálása
    • A tizedesjegyeket tetszőleges számú számjegyre kerekítheti.
    • Egész számok kerekítése tetszőleges számú számjegyre
    • Megjegyzés: Negatív értékek esetén

Vegyük észre, hogy mint fentebb említettük, a beépített round függvény nem általános kerekítés, hanem páros számra kerekítés. A részleteket lásd alább.

beépített függvény (pl. programozási nyelvben): round()

A Round() beépített függvény. Modulok importálása nélkül használható.

Az első argumentum az eredeti szám, a második pedig a számjegyek száma (hány számjegyre kell kerekíteni).

A tizedesjegyeket tetszőleges számú számjegyre kerekítheti.

Az alábbiakban egy példa a lebegőpontos lebegőpontos típus feldolgozására.

Ha a második argumentum kimarad, akkor egész számra kerekít. A típus is integer int típus lesz.

f = 123.456

print(round(f))
# 123

print(type(round(f)))
# <class 'int'>

Ha a második argumentum meg van adva, akkor egy lebegőpontos lebegőpontos típust ad vissza.

Pozitív egész szám megadása esetén a tizedesjegyet, negatív egész szám megadása esetén az egész szám helyét kell megadni. A -1 a legközelebbi tizedre kerekít, a -2 a legközelebbi századra kerekít, a 0 pedig egész számra kerekít (az első helyre), de egy float típust ad vissza, ellentétben azzal, ha elhagyja.

print(round(f, 1))
# 123.5

print(round(f, 2))
# 123.46

print(round(f, -1))
# 120.0

print(round(f, -2))
# 100.0

print(round(f, 0))
# 123.0

print(type(round(f, 0)))
# <class 'float'>

Egész számok kerekítése tetszőleges számú számjegyre.

A következő példa az integer int típus feldolgozására.

Ha a második argumentum kimarad, vagy ha 0 vagy pozitív egész szám van megadva, az eredeti értéket változatlanul visszaadja. Ha negatív egész számot adunk meg, akkor a megfelelő egész számjegyre kerekítünk. Mindkét esetben egy integer int típusú értéket kapunk vissza.

i = 99518

print(round(i))
# 99518

print(round(i, 2))
# 99518

print(round(i, -1))
# 99520

print(round(i, -2))
# 99500

print(round(i, -3))
# 100000

round() páros számra kerekít, nem pedig egy közös kerekítésre.

Vegye figyelembe, hogy a Python 3-ban a beépített round() függvénnyel történő kerekítés páros számra kerekít, nem pedig általános kerekítésre.

A hivatalos dokumentáció szerint a 0,5 0-ra kerekített, az 5 0-ra kerekített, és így tovább.

print('0.4 =>', round(0.4))
print('0.5 =>', round(0.5))
print('0.6 =>', round(0.6))
# 0.4 => 0
# 0.5 => 0
# 0.6 => 1

print('4 =>', round(4, -1))
print('5 =>', round(5, -1))
print('6 =>', round(6, -1))
# 4 => 0
# 5 => 0
# 6 => 10

A páros számra kerekítés definíciója a következő.

Ha a tört 0,5-nél kisebb, kerekítse lefelé; ha a tört 0,5-nél nagyobb, kerekítse felfelé; ha a tört pontosan 0,5, kerekítse fel a le- és felkerekítés közötti páros számra.
Rounding – Wikipedia

A 0,5 nem mindig csonka.

print('0.5 =>', round(0.5))
print('1.5 =>', round(1.5))
print('2.5 =>', round(2.5))
print('3.5 =>', round(3.5))
print('4.5 =>', round(4.5))
# 0.5 => 0
# 1.5 => 2
# 2.5 => 2
# 3.5 => 4
# 4.5 => 4

Bizonyos esetekben a páros számra kerekítés definíciója nem is vonatkozik a két tizedesjegy utáni feldolgozásra.

print('0.05 =>', round(0.05, 1))
print('0.15 =>', round(0.15, 1))
print('0.25 =>', round(0.25, 1))
print('0.35 =>', round(0.35, 1))
print('0.45 =>', round(0.45, 1))
# 0.05 => 0.1
# 0.15 => 0.1
# 0.25 => 0.2
# 0.35 => 0.3
# 0.45 => 0.5

Ennek oka, hogy a tizedesjegyek nem ábrázolhatók pontosan lebegőpontos számokként, ahogyan azt a hivatalos dokumentációban is szerepel.

A round() viselkedése lebegőpontos számok esetén meglepő lehet.:Például a round(2.675, 2) a várt 2.68 helyett 2.67-et ad. Ez nem hiba.:Ez annak a ténynek az eredménye, hogy a legtöbb tizedesjegy nem ábrázolható pontosan lebegőpontos számokkal.
round() — Built-in Functions — Python 3.10.2 Documentation

Ha általános kerekítést vagy a tizedesjegyek páros számokra való pontos kerekítését szeretné elérni, használhatja a szabványos könyvtár decimális kvantálását (lásd alább), vagy definiálhat egy új függvényt.

Vegyük észre azt is, hogy a Python 2-ben a round() nem páros számra kerekít, hanem kerekít.

a szabványos könyvtár decimál kvantálás() funkciója

A szabványos könyvtár decimal modulja használható a pontos decimális lebegőpontos számok kezelésére.

A decimális modul kvantálás() metódusának használatával a kerekítési mód megadásával kerekíthetjük a számokat.

A kvantálás() módszer kerekítési argumentumának beállított értékei a következő jelentéssel bírnak.

  • ROUND_HALF_UP:Általános kerekítés
  • ROUND_HALF_EVEN:Páros számokra kerekítés

A decimal modul egy szabványos könyvtár, így nincs szükség további telepítésre, de az importálás szükséges.

from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN

Decimal objektum létrehozása

A Decimal() segítségével Decimal típusú objektumok hozhatók létre.

Ha egy float típust ad meg argumentumként, akkor láthatja, hogy az értéket valójában milyen értékként kezeli.

print(Decimal(0.05))
# 0.05000000000000000277555756156289135105907917022705078125

print(type(Decimal(0.05)))
# <class 'decimal.Decimal'>

Ahogy a példában látható, a 0,05 nem pontosan 0,05-nek tekintendő. Ez az oka annak, hogy a fent leírt beépített round() függvény a példában szereplő 0,05-öt is tartalmazó tizedes értékek esetén a várttól eltérő értékre kerekít.

Mivel a 0,5 a fele (2 -1 hatványa), pontosan kifejezhető bináris jelöléssel.

print(Decimal(0.5))
# 0.5

Ha a float típus helyett a str string típust adja meg, akkor a program a pontos érték Decimal típusaként kezeli.

print(Decimal('0.05'))
# 0.05

Tizedesjegyek kerekítése tetszőleges számjegyekre és kerekítés páros számokra

Hívja meg a quantize() funkciót egy Decimal típusú objektumból az érték kerekítéséhez.

A quantize() első argumentuma egy olyan karakterlánc, amelynek számjegyeinek száma megegyezik a keresett számjegyek számával, például '0.1' vagy '0.01'.

Ezen kívül a ROUNDING argumentum meghatározza a kerekítési módot; ha a ROUND_HALF_UP van megadva, akkor általános kerekítést használ.

f = 123.456

print(Decimal(str(f)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
# 123

print(Decimal(str(f)).quantize(Decimal('0.1'), rounding=ROUND_HALF_UP))
# 123.5

print(Decimal(str(f)).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))
# 123.46

A beépített round() függvénytől eltérően a 0,5 1-re kerül kerekítésre.

print('0.4 =>', Decimal(str(0.4)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
print('0.5 =>', Decimal(str(0.5)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
print('0.6 =>', Decimal(str(0.6)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
# 0.4 => 0
# 0.5 => 1
# 0.6 => 1

Ha a kerekítés argumentum ROUND_HALF_EVEN értékre van állítva, a kerekítés páros számokra történik, mint a beépített round() függvényben.

Mint fentebb említettük, ha a Decimal() argumentumaként egy lebegőpontos lebegőpontos típus van megadva, akkor azt egy Decimal objektumként kezeli, amelynek értéke megegyezik a lebegőpontos típus tényleges értékével, így a kvantálás() metódus használatának eredménye eltér a várttól, akárcsak a beépített round() függvényé.

print('0.05 =>', round(0.05, 1))
print('0.15 =>', round(0.15, 1))
print('0.25 =>', round(0.25, 1))
print('0.35 =>', round(0.35, 1))
print('0.45 =>', round(0.45, 1))
# 0.05 => 0.1
# 0.15 => 0.1
# 0.25 => 0.2
# 0.35 => 0.3
# 0.45 => 0.5

print('0.05 =>', Decimal(0.05).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.15 =>', Decimal(0.15).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.25 =>', Decimal(0.25).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.35 =>', Decimal(0.35).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.45 =>', Decimal(0.45).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
# 0.05 => 0.1
# 0.15 => 0.1
# 0.25 => 0.2
# 0.35 => 0.3
# 0.45 => 0.5

Ha a Decimal() argumentuma str típusú karakterláncként van megadva, a program pontosan ilyen értékű Decimal objektumként kezeli, így az eredmény a vártnak megfelelően alakul.

print('0.05 =>', Decimal(str(0.05)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.15 =>', Decimal(str(0.15)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.25 =>', Decimal(str(0.25)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.35 =>', Decimal(str(0.35)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.45 =>', Decimal(str(0.45)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
# 0.05 => 0.0
# 0.15 => 0.2
# 0.25 => 0.2
# 0.35 => 0.4
# 0.45 => 0.4

Mivel a 0,5 helyesen kezelhető a float típussal, nem jelent problémát, ha egész számra kerekítéskor a Decimal() argumentumaként a float típust adjuk meg, de biztonságosabb a string str típust megadni, ha tizedesre kerekítünk.

Például a 2,675 valójában 2,67499…. float típusban. Ezért ha két tizedesjegyre akar kerekíteni, akkor a Decimal()-nak meg kell adnia egy karakterláncot, különben az eredmény eltér a várt eredménytől, akár a legközelebbi egész számra (ROUND_HALF_UP), akár páros számra (ROUND_HALF_EVEN) kerekít.

print(Decimal(2.675))
# 2.67499999999999982236431605997495353221893310546875

print(Decimal(2.675).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))
# 2.67

print(Decimal(str(2.675)).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))
# 2.68

print(Decimal(2.675).quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN))
# 2.67

print(Decimal(str(2.675)).quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN))
# 2.68

Vegye figyelembe, hogy a quantize() metódus egy Decimal típusú számot ad vissza, így ha egy float típusú számmal akar operálni, akkor azt a float() segítségével kell float típusúvá alakítania, különben hiba lép fel.

d = Decimal('123.456').quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)

print(d)
# 123.46

print(type(d))
# <class 'decimal.Decimal'>

# print(1.2 + d)
# TypeError: unsupported operand type(s) for +: 'float' and 'decimal.Decimal'

print(1.2 + float(d))
# 124.66

Egész számok kerekítése tetszőleges számú számjegyre és kerekítés páros számokra

Ha egész számjegyre szeretnénk kerekíteni, az első argumentumként megadott '10' nem adja meg a kívánt eredményt.

i = 99518

print(Decimal(i).quantize(Decimal('10'), rounding=ROUND_HALF_UP))
# 99518

Ennek az az oka, hogy a quantize() a kerekítést a Decimal objektum exponensének megfelelően végzi, de a Decimal('10') exponensének értéke 0, nem pedig 1.

Tetszőleges exponens megadható az E exponens karakterlánc használatával (pl. '1E1'). Az exponens exponens az as_tuple metódusban ellenőrizhető.

print(Decimal('10').as_tuple())
# DecimalTuple(sign=0, digits=(1, 0), exponent=0)

print(Decimal('1E1').as_tuple())
# DecimalTuple(sign=0, digits=(1,), exponent=1)

Így az eredmény exponenciális jelölés lesz az E használatával. Ha normál jelölést szeretne használni, vagy ha kerekítés után integer int típussal akar operálni, használja az int() funkciót az eredmény átalakítására.

print(Decimal(i).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP))
# 9.952E+4

print(int(Decimal(i).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
# 99520

print(int(Decimal(i).quantize(Decimal('1E2'), rounding=ROUND_HALF_UP)))
# 99500

print(int(Decimal(i).quantize(Decimal('1E3'), rounding=ROUND_HALF_UP)))
# 100000

Ha a kerekítés argumentum ROUND_HALF_UP értékre van állítva, akkor általános kerekítésre kerül sor, például az 5 értéket 10-re kerekíti.

print('4 =>', int(Decimal(4).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
print('5 =>', int(Decimal(5).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
print('6 =>', int(Decimal(6).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
# 4 => 0
# 5 => 10
# 6 => 10

Természetesen nem jelent problémát, ha karakterláncként adja meg.

Új függvény definiálása

A decimális modul használatának módszere pontos és biztonságos, de ha a típuskonverzióval nincs megelégedve, egy új függvényt is definiálhat az általános kerekítés eléréséhez.

Ennek számos lehetséges módja van, például a következő függvény.

def my_round(val, digit=0):
    p = 10 ** digit
    return (val * p * 2 + 1) // 2 / p

Ha nincs szükség a számjegyek számának megadására, és mindig az első tizedesjegyre kerekít, használhat egy egyszerűbb formát.

my_round_int = lambda x: int((x * 2 + 1) // 2)

Ha precíznek kell lennie, biztonságosabb a tizedesjegyek használata.

A következők csak tájékoztató jellegűek.

A tizedesjegyeket tetszőleges számú számjegyre kerekítheti.

print(int(my_round(f)))
# 123

print(my_round_int(f))
# 123

print(my_round(f, 1))
# 123.5

print(my_round(f, 2))
# 123.46

A kerekítéssel ellentétben a 0,5 az általános kerekítésnek megfelelően 1 lesz.

print(int(my_round(0.4)))
print(int(my_round(0.5)))
print(int(my_round(0.6)))
# 0
# 1
# 1

Egész számok kerekítése tetszőleges számú számjegyre

i = 99518

print(int(my_round(i, -1)))
# 99520

print(int(my_round(i, -2)))
# 99500

print(int(my_round(i, -3)))
# 100000

A kerekítéssel ellentétben az 5-ből 10 lesz a szokásos kerekítés szerint.

print(int(my_round(4, -1)))
print(int(my_round(5, -1)))
print(int(my_round(6, -1)))
# 0
# 10
# 10

Megjegyzés: Negatív értékek esetén

A fenti példafüggvényben a -0,5 0-ra van kerekítve.

print(int(my_round(-0.4)))
print(int(my_round(-0.5)))
print(int(my_round(-0.6)))
# 0
# 0
# -1

A negatív értékek kerekítésére többféleképpen is gondolhatunk, de ha a -0,5-ből -1-et akarunk csinálni, akkor például a következőképpen módosíthatjuk a kerekítést

import math

def my_round2(val, digit=0):
    p = 10 ** digit
    s = math.copysign(1, val)
    return (s * val * p * 2 + 1) // 2 / p * s

print(int(my_round2(-0.4)))
print(int(my_round2(-0.5)))
print(int(my_round2(-0.6)))
# 0
# -1
# -1
Copied title and URL