Python’da ondalık sayıları ve tam sayıları “yuvarlak” ve “Decimal.quantize” ile yuvarlama

Aşağıda Python’da sayıların çift sayıya yuvarlama veya yuvarlama yoluyla nasıl yuvarlanacağı açıklanmaktadır. Sayıların kayan noktalı kayan nokta veya tamsayı int türünde olduğu varsayılır.

  • yerleşik işlev (örneğin programlama dilinde):round()
    • Ondalık sayıları herhangi bir sayıda basamağa yuvarlayın.
    • Tam sayıları herhangi bir sayıda basamağa yuvarlayın.
    • round() ortak bir yuvarlamaya değil, çift sayıya yuvarlar
  • standart kitaplıkdecimalquantize()
    • DecimalBir nesne oluşturma
    • Ondalık sayıları herhangi bir sayıda basamağa ve çift sayılara yuvarlama
    • Tam sayıları herhangi bir basamağa ve çift sayılara yuvarlama
  • Yeni bir işlev tanımlayın
    • Ondalık sayıları herhangi bir sayıda basamağa yuvarlayın.
    • Tam sayıları herhangi bir sayıda basamağa yuvarlama
    • Not: Negatif değerler için

Yukarıda belirtildiği gibi, yerleşik işlev turunun genel bir yuvarlama değil, çift sayıya yuvarlama olduğuna dikkat edin. Ayrıntılar için aşağıya bakın.

yerleşik işlev (örneğin programlama dilinde):round()

Round() yerleşik bir işlev olarak sağlanır. Herhangi bir modül içe aktarmadan kullanılabilir.

İlk argüman orijinal sayıdır ve ikinci argüman basamak sayısıdır (kaç basamağa yuvarlanacak).

Ondalık sayıları herhangi bir sayıda basamağa yuvarlayın.

Aşağıdaki, kayan noktalı kayan nokta türü için bir işleme örneğidir.

İkinci argüman atlanırsa, bir tamsayıya yuvarlanır. Tür ayrıca bir tamsayı int türü olur.

f = 123.456

print(round(f))
# 123

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

İkinci argüman belirtilirse, kayan noktalı bir kayan nokta türü döndürür.

Pozitif bir tamsayı belirtilirse, ondalık basamak belirtilir; negatif bir tamsayı belirtilirse, tamsayı yeri belirtilir. -1 en yakın ondalığa yuvarlar, -2 en yakın yüzlüğe yuvarlar ve 0 bir tamsayıya yuvarlar (ilk yer), ancak atlandığından farklı olarak bir kayan nokta türü döndürür.

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

Tam sayıları herhangi bir sayıda basamağa yuvarlayın.

Aşağıda tamsayı int türü için bir işleme örneği verilmiştir.

İkinci argüman atlanırsa veya 0 veya pozitif bir tamsayı belirtilirse, orijinal değer olduğu gibi döndürülür. Negatif bir tamsayı belirtilirse, karşılık gelen tamsayı basamağına yuvarlanır. Her iki durumda da bir tamsayı int türü döndürülür.

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() ortak bir yuvarlamaya değil, çift sayıya yuvarlar

Python 3’teki yerleşik round() işleviyle yuvarlamanın genel bir yuvarlamaya değil, çift sayıya yuvarlandığını unutmayın.

Resmi belgelerde yazıldığı gibi 0,5, 0’a yuvarlanır, 5, 0’a yuvarlanır vb.

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

Çift sayıya yuvarlamanın tanımı aşağıdaki gibidir.

Kesir 0,5’ten küçükse aşağı yuvarlayın; kesir 0,5’ten büyükse, yukarı yuvarlayın; kesir tam olarak 0,5 ise, aşağı ve yukarı yuvarlama arasındaki çift sayıya yuvarlayın.
Rounding – Wikipedia

0,5 her zaman kesilmez.

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

Bazı durumlarda, çift sayıya yuvarlama tanımı, iki ondalık basamaktan sonra yapılan işlemler için bile geçerli değildir.

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

Bunun nedeni, resmi belgelerde belirtildiği gibi ondalık sayıların tam olarak kayan noktalı sayılar olarak temsil edilememesidir.

Round()’un kayan noktalı sayılardaki davranışı sizi şaşırtabilir:Örneğin, round(2.675, 2) size beklendiği gibi 2.68 yerine 2.67 verecektir. Bu bir hata değil.:Bu, çoğu ondalık sayının tam olarak kayan nokta sayılarıyla temsil edilememesinin bir sonucudur.
round() — Built-in Functions — Python 3.10.2 Documentation

Genel yuvarlama veya ondalık sayıları çift sayılara doğru yuvarlama elde etmek istiyorsanız, standart ondalık niceleme kitaplığını (aşağıda açıklanmıştır) kullanabilir veya yeni bir işlev tanımlayabilirsiniz.

Ayrıca Python 2’deki round() öğesinin çift sayıya değil yuvarlama olduğuna dikkat edin.

standart kitaplığın ondalık sayısının nicelleştirilmesi()

Standart kitaplığın ondalık modülü, tam ondalık kayan nokta sayılarını işlemek için kullanılabilir.

Ondalık modülün quantize() yöntemini kullanarak, yuvarlama modunu belirterek sayıları yuvarlamak mümkündür.

quantize() yönteminin bağımsız değişken yuvarlaması için ayar değerleri sırasıyla aşağıdaki anlamlara sahiptir.

  • ROUND_HALF_UP:Genel yuvarlama
  • ROUND_HALF_EVEN:Çift sayılara yuvarlama

Ondalık modül standart bir kitaplıktır, bu nedenle ek kurulum gerekmez, ancak içe aktarma gereklidir.

from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN

Decimal nesnesi oluşturma

Decimal(), Decimal türünde nesneler oluşturmak için kullanılabilir.

Argüman olarak bir kayan nokta türü belirtirseniz, değerin gerçekte ne olarak ele alındığını görebilirsiniz.

print(Decimal(0.05))
# 0.05000000000000000277555756156289135105907917022705078125

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

Örnekte gösterildiği gibi, 0,05 tam olarak 0,05 olarak değerlendirilmez. Yukarıda açıklanan yerleşik yuvarlak() işlevinin, örnekte 0,05 dahil olmak üzere ondalık değerler için beklenenden farklı bir değere yuvarlanmasının nedeni budur.

0,5, yarım (-1 kuvveti 2’nin) olduğundan, tam olarak ikili gösterimde ifade edilebilir.

print(Decimal(0.5))
# 0.5

Float türü yerine str dize türünü belirtirseniz, tam değerin Ondalık türü olarak değerlendirilir.

print(Decimal('0.05'))
# 0.05

Ondalık sayıları herhangi bir sayıda basamağa ve çift sayılara yuvarlama

Değeri yuvarlamak için Decimal türünde bir nesneden quantize() öğesini çağırın.

quantize() öğesinin ilk argümanı, ‘0.1’ veya ‘0.01’ gibi bulmak istediğiniz basamak sayısıyla aynı sayıda basamak içeren bir dizedir.

Ayrıca, ROUNDING bağımsız değişkeni yuvarlama modunu belirtir; ROUND_HALF_UP belirtilirse, genel yuvarlama kullanılır.

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

Yerleşik yuvarlak() işlevinden farklı olarak 0,5, 1’e yuvarlanır.

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

Yuvarlama bağımsız değişkeni ROUND_HALF_EVEN olarak ayarlanırsa, yerleşik yuvarlak() işlevinde olduğu gibi çift sayılara yuvarlama yapılır.

Yukarıda bahsedildiği gibi, Decimal() argümanı olarak bir kayan noktalı kayan nokta türü belirtilirse, kayan noktalı kayan noktalı kayan nokta türü, kayan noktalı türün gerçek değerine eşit bir değere sahip bir Decimal nesnesi olarak kabul edilir, dolayısıyla quantize() kullanımının sonucu yöntem, tıpkı yerleşik işlev round() gibi, beklenenden farklı olacaktır.

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

Decimal() argümanı str türünde bir dize olarak belirtilirse, tam olarak bu değere sahip bir Decimal nesnesi olarak kabul edilir, bu nedenle sonuç beklendiği gibi olur.

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

0,5, kayan nokta türü tarafından doğru bir şekilde işlenebildiğinden, bir tamsayıya yuvarlarken Decimal() argümanı olarak kayan nokta türünü belirtmekte sorun yoktur, ancak bir ondalık basamağa yuvarlarken dize türünü belirtmek daha güvenlidir.

Örneğin, 2.675 aslında float türünde 2.67499….’dur. Bu nedenle, iki ondalık basamağa yuvarlamak istiyorsanız Decimal()’e bir dize belirtmelisiniz, aksi takdirde sonuç, en yakın tam sayıya (ROUND_HALF_UP) veya çift sayıya (ROUND_HALF_EVEN) yuvarlasanız da beklenen sonuçtan farklı olacaktır. ).

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

quantize() yönteminin bir Decimal tür numarası döndürdüğünü unutmayın, bu nedenle bir kayan noktalı sayı üzerinde çalışmak istiyorsanız, onu kayan nokta() kullanarak bir kayan noktalı sayıya dönüştürmeniz gerekir, aksi takdirde bir hata oluşur.

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

Tam sayıları herhangi bir basamağa ve çift sayılara yuvarlama

Bir tamsayı basamağa yuvarlamak istiyorsanız, ilk argüman olarak ’10’ gibi bir şey belirtmek size istenen sonucu vermeyecektir.

i = 99518

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

Bunun nedeni, quantize() öğesinin Decimal nesnesinin üssüne göre yuvarlama gerçekleştirmesidir, ancak Decimal(’10’) öğesinin üssü 1 değil 0’dır.

Üs dizesi olarak E’yi kullanarak isteğe bağlı bir üs belirtebilirsiniz (örneğin, ‘1E1’). Üs üssü as_tuple yönteminde kontrol edilebilir.

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)

Olduğu gibi, sonuç E kullanılarak üstel gösterimde olacaktır. Normal gösterim kullanmak istiyorsanız veya yuvarlamadan sonra tamsayı int türü ile çalışmak istiyorsanız, sonucu dönüştürmek için int() kullanın.

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

Argüman yuvarlaması ROUND_HALF_UP olarak ayarlanırsa, genel yuvarlama gerçekleşir, örneğin 5, 10’a yuvarlanır.

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

Tabii bunu string olarak belirtirseniz bir problem olmaz.

Yeni bir işlev tanımlayın

Ondalık modülü kullanma yöntemi doğru ve güvenlidir, ancak tür dönüştürme konusunda rahat değilseniz, genel yuvarlama elde etmek için yeni bir işlev tanımlayabilirsiniz.

Bunu yapmanın birçok olası yolu vardır, örneğin aşağıdaki işlev.

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

Basamak sayısını belirtmeniz gerekmiyorsa ve her zaman ilk ondalık basamağa yuvarlarsanız, daha basit bir form kullanabilirsiniz.

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

Kesin olmanız gerekiyorsa, ondalık kullanmak daha güvenlidir.

Aşağıdaki sadece referans içindir.

Ondalık sayıları herhangi bir sayıda basamağa yuvarlayın.

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

Turdan farklı olarak, genel yuvarlamada 0,5 1 olur.

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

Tam sayıları herhangi bir sayıda basamağa yuvarlama

i = 99518

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

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

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

Turdan farklı olarak, genel yuvarlama uyarınca 5, 10 olur.

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

Not: Negatif değerler için

Yukarıdaki örnek fonksiyonda -0.5, 0’a yuvarlanır.

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

Negatif değerler için yuvarlamayı düşünmenin çeşitli yolları vardır, ancak -0.5’i -1’e yapmak istiyorsanız, bunu aşağıdaki gibi değiştirebilirsiniz, örneğin

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