Zaokruživanje decimala i cijelih brojeva u Pythonu pomoću “round” i “Decimal.quantize”

Poslovanje

Sljedeće objašnjava kako zaokružiti brojeve u Pythonu zaokružujući ili zaokružujući na paran broj. Pretpostavlja se da su brojevi tipa float s pomičnim zarezom ili integer int.

  • ugrađena funkcija (npr. u programskom jeziku):round()
    • Zaokružite decimale na bilo koji broj znamenki.
    • Zaokružite cijele brojeve na bilo koji broj znamenki.
    • round() zaokružuje na paran broj, a ne na uobičajeno zaokruživanje
  • standardna bibliotekadecimalquantize()
    • DecimalStvaranje objekta
    • Zaokruživanje decimala na bilo koji broj znamenki i zaokruživanje na parne brojeve
    • Zaokruživanje cijelih brojeva na bilo koji broj znamenki i zaokruživanje na parne brojeve
  • Definirajte novu funkciju
    • Zaokružite decimale na bilo koji broj znamenki.
    • Zaokružite cijele brojeve na bilo koji broj znamenki
    • Napomena: Za negativne vrijednosti

Imajte na umu da, kao što je gore spomenuto, zaokruživanje ugrađene funkcije nije općenito zaokruživanje, već zaokruživanje na paran broj. Za detalje pogledajte u nastavku.

ugrađena funkcija (npr. u programskom jeziku):round()

Round() se nudi kao ugrađena funkcija. Može se koristiti bez uvoza modula.

Prvi argument je izvorni broj, a drugi argument je broj znamenki (na koliko znamenki zaokružiti).

Zaokružite decimale na bilo koji broj znamenki.

Sljedeći je primjer obrade za tip float s pomičnim zarezom.

Ako je drugi argument izostavljen, zaokružuje se na cijeli broj. Tip također postaje cjelobrojni int tip.

f = 123.456

print(round(f))
# 123

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

Ako je naveden drugi argument, on vraća tip float s pomičnim zarezom.

Ako je naveden pozitivan cijeli broj, navedeno je decimalno mjesto; ako je naveden negativan cijeli broj, navedeno je cijelobrojno mjesto. -1 zaokružuje na najbližu desetinu, -2 zaokružuje na najbližu stotinu i 0 zaokružuje na cijeli broj (prvo mjesto), ali vraća tip s plutajućim brojem, za razliku od kada je izostavljen.

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'>

Zaokružite cijele brojeve na bilo koji broj znamenki.

Sljedeći je primjer obrade za integer int tip.

Ako je drugi argument izostavljen ili ako je naveden 0 ili pozitivan cijeli broj, izvorna vrijednost se vraća onakva kakva jest. Ako je naveden negativan cijeli broj, zaokružuje se na odgovarajuću cjelobrojnu znamenku. U oba slučaja vraća se cijeli broj tipa int.

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() zaokružuje na paran broj, a ne na uobičajeno zaokruživanje

Imajte na umu da se zaokruživanje s ugrađenom funkcijom round() u Pythonu 3 zaokružuje na paran broj, a ne na općenito zaokruživanje.

Kao što je napisano u službenoj dokumentaciji, 0,5 se zaokružuje na 0, 5 se zaokružuje na 0, i tako dalje.

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

Definicija zaokruživanja na paran broj je sljedeća.

Ako je razlomak manji od 0,5, zaokružite ga prema dolje; ako je udio veći od 0,5, zaokružite ga prema gore; ako je razlomak točno 0,5, zaokružite ga na paran broj između zaokruživanja prema dolje i zaokruživanju prema gore.
Rounding – Wikipedia

0,5 nije uvijek skraćeno.

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

U nekim slučajevima definicija zaokruživanja na paran broj ne vrijedi ni za obradu nakon dva decimalna mjesta.

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

To je zbog činjenice da se decimale ne mogu točno prikazati kao brojevi s pomičnim zarezom, kao što je navedeno u službenoj dokumentaciji.

Ponašanje round() za brojeve s pomičnim zarezom može vas iznenaditi:Na primjer, krug (2,675, 2) će vam dati 2,67 umjesto 2,68 kako se očekivalo. Ovo nije bug.:To je rezultat činjenice da se većina decimala ne može točno prikazati brojevima s pomičnim zarezom.
round() — Built-in Functions — Python 3.10.2 Documentation

Ako želite postići općenito zaokruživanje ili točno zaokruživanje decimala na parne brojeve, možete koristiti standardnu ​​knjižnicu decimalnog kvantiziranja (opisano u nastavku) ili definirati novu funkciju.

Također imajte na umu da round() u Pythonu 2 nije zaokruživanje na paran broj, već zaokruživanje.

quantize() decimale standardne biblioteke

Decimalni modul standardne biblioteke može se koristiti za rukovanje točnim decimalnim brojevima s pomičnim zarezom.

Koristeći quantize() metodu decimalnog modula, moguće je zaokružiti brojeve specificiranjem načina zaokruživanja.

Postavljene vrijednosti za zaokruživanje argumenata metode quantize() imaju sljedeća značenja.

  • ROUND_HALF_UP:Opće zaokruživanje
  • ROUND_HALF_EVEN:Zaokruživanje na parne brojeve

Decimalni modul je standardna biblioteka, tako da nije potrebna dodatna instalacija, ali je neophodan uvoz.

from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN

Stvaranje decimalnog objekta

Decimal() se može koristiti za kreiranje objekata tipa Decimal.

Ako navedete tip float kao argument, možete vidjeti kako se vrijednost zapravo tretira.

print(Decimal(0.05))
# 0.05000000000000000277555756156289135105907917022705078125

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

Kao što je prikazano u primjeru, 0,05 se ne tretira kao točno 0,05. To je razlog zašto je ugrađena funkcija round() opisana gore zaokružila na vrijednost različitu od očekivane za decimalne vrijednosti uključujući 0,05 u primjeru.

Budući da je 0,5 jedna polovica (-1 stepen od 2), može se točno izraziti u binarnom zapisu.

print(Decimal(0.5))
# 0.5

Ako navedete vrstu stringa str umjesto tipa float, on će se tretirati kao decimalni tip točne vrijednosti.

print(Decimal('0.05'))
# 0.05

Zaokruživanje decimala na bilo koji broj znamenki i zaokruživanje na parne brojeve

Pozovite quantize() iz objekta tipa Decimal da biste zaokružili vrijednost.

Prvi argument quantize() je niz s istim brojem znamenki kao i broj znamenki koje želite pronaći, kao što je ‘0,1’ ili ‘0,01’.

Dodatno, argument ROUNDING specificira način zaokruživanja; ako je navedeno ROUND_HALF_UP, koristi se opće zaokruživanje.

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

Za razliku od ugrađene funkcije round(), 0,5 se zaokružuje na 1.

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

Ako je zaokruživanje argumenta postavljeno na ROUND_HALF_EVEN, zaokruživanje se izvodi na parne brojeve kao u ugrađenoj funkciji round().

Kao što je gore spomenuto, ako je tip float s pomičnim zarezom naveden kao argument Decimal(), on se tretira kao Decimal objekt s vrijednošću jednakom stvarnoj vrijednosti tipa float, tako da je rezultat korištenja quantize() metoda će se razlikovati od očekivanog, baš kao i ugrađena funkcija round().

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

Ako je argument Decimal() naveden kao niz tipa str, on se tretira kao Decimalni objekt točno te vrijednosti, tako da je rezultat očekivan.

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

Budući da se tipom float može ispravno rukovati 0,5, nema problema u određivanju tipa float kao argumenta Decimal() kada se zaokružuje na cijeli broj, ali je sigurnije navesti string tip str kada se zaokružuje na decimalno mjesto.

Na primjer, 2,675 je zapravo 2,67499…. u float tipu. Stoga, ako želite zaokružiti na dvije decimale, morate navesti niz na Decimal(), inače će rezultat biti drugačiji od očekivanog rezultata bez obzira zaokružite li na najbliži cijeli broj (ROUND_HALF_UP) ili na paran broj (ROUND_HALF_EVEN ).

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

Imajte na umu da metoda quantize() vraća broj tipa Decimal, tako da ako želite raditi s brojem s plutajućim brojem, trebate ga pretvoriti u float tip pomoću float(), inače će doći do pogreške.

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

Zaokruživanje cijelih brojeva na bilo koji broj znamenki i zaokruživanje na parne brojeve

Ako želite zaokružiti na cjelobrojnu znamenku, navođenjem nečega poput ’10’ kao prvog argumenta nećete dobiti željeni rezultat.

i = 99518

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

To je zato što quantize() izvodi zaokruživanje prema eksponentu Decimal objekta, ali eksponent Decimal(’10’) je 0, a ne 1.

Možete odrediti proizvoljan eksponent korištenjem E kao stringa eksponenta (npr. ‘1E1’). Eksponent eksponenta može se provjeriti metodom as_tuple.

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)

Takav kakav jest, rezultat će biti u eksponencijalnom zapisu koristeći E. Ako želite koristiti normalnu notaciju, ili ako želite raditi s int int tipom nakon zaokruživanja, upotrijebite int() da pretvorite rezultat.

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

Ako je zaokruživanje argumenta postavljeno na ROUND_HALF_UP, doći će do općeg zaokruživanja, npr. 5 će se zaokružiti na 10.

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

Naravno, nema problema ako ga navedete kao niz.

Definirajte novu funkciju

Metoda korištenja decimalnog modula je točna i sigurna, ali ako vam ne odgovara pretvorba tipa, možete definirati novu funkciju za postizanje općeg zaokruživanja.

Postoji mnogo mogućih načina za to, na primjer, sljedeća funkcija.

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

Ako ne trebate specificirati broj znamenki i uvijek zaokružiti na prvu decimalu, možete koristiti jednostavniji obrazac.

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

Ako trebate biti precizni, sigurnije je koristiti decimalni.

Sljedeće je samo za referencu.

Zaokružite decimale na bilo koji broj znamenki.

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

Za razliku od kruga, 0,5 postaje 1 prema općem zaokruživanju.

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

Zaokružite cijele brojeve na bilo koji broj znamenki

i = 99518

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

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

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

Za razliku od kruga, 5 postaje 10 prema uobičajenom zaokruživanju.

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

Napomena: Za negativne vrijednosti

U gornjem primjeru funkcije, -0,5 je zaokruženo na 0.

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

Postoje različiti načini razmišljanja o zaokruživanju za negativne vrijednosti, ali ako želite pretvoriti -0,5 u -1, možete to izmijeniti na sljedeći način, na primjer

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