การปัดเศษทศนิยมและจำนวนเต็มใน Python ด้วย “round” และ “Decimal.quantize

ธุรกิจ

ข้อมูลต่อไปนี้จะอธิบายวิธีการปัดเศษตัวเลขใน Python โดยการปัดเศษหรือปัดเศษเป็นเลขคู่ ตัวเลขจะถือว่าเป็นทศนิยมทศนิยมหรือประเภท int จำนวนเต็ม

  • ฟังก์ชันในตัว (เช่น ในภาษาโปรแกรม):round()
    • ปัดเศษทศนิยมเป็นจำนวนหลักเท่าใดก็ได้
    • ปัดเศษจำนวนเต็มเป็นจำนวนหลักเท่าใดก็ได้
    • round() ปัดเศษเป็นเลขคู่ ไม่ใช่การปัดเศษทั่วไป
  • ห้องสมุดมาตรฐานdecimalquantize()
    • Decimalการสร้างวัตถุ
    • การปัดเศษทศนิยมเป็นจำนวนหลักใด ๆ และการปัดเศษเป็นเลขคู่
    • การปัดเศษจำนวนเต็มเป็นจำนวนหลักใดๆ และการปัดเศษเป็นเลขคู่
  • กำหนดฟังก์ชันใหม่
    • ปัดเศษทศนิยมเป็นจำนวนหลักเท่าใดก็ได้
    • ปัดเศษจำนวนเต็มเป็นจำนวนหลัก
    • หมายเหตุ: สำหรับค่าลบ

โปรดทราบว่าดังที่กล่าวไว้ข้างต้น ฟังก์ชัน Round ในตัวไม่ใช่การปัดเศษทั่วไป แต่เป็นการปัดเศษเป็นจำนวนคู่ ดูรายละเอียดด้านล่าง

ฟังก์ชันในตัว (เช่น ในภาษาโปรแกรม):round()

Round() มีให้เป็นฟังก์ชันในตัว สามารถใช้งานได้โดยไม่ต้องนำเข้าโมดูลใดๆ

อาร์กิวเมนต์แรกคือตัวเลขดั้งเดิม และอาร์กิวเมนต์ที่สองคือจำนวนหลัก (จำนวนหลักที่จะปัดเศษ)

ปัดเศษทศนิยมเป็นจำนวนหลักเท่าใดก็ได้

ต่อไปนี้คือตัวอย่างการประมวลผลสำหรับประเภททศนิยมทศนิยม

หากละเว้นอาร์กิวเมนต์ที่สอง จะมีการปัดเศษเป็นจำนวนเต็ม ชนิดนี้ยังกลายเป็นชนิดจำนวนเต็มด้วย

f = 123.456

print(round(f))
# 123

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

ถ้าอาร์กิวเมนต์ที่สองถูกระบุ จะส่งกลับประเภททศนิยมทศนิยม

หากระบุจำนวนเต็มบวก จะมีการระบุตำแหน่งทศนิยม หากระบุจำนวนเต็มลบ จะมีการระบุตำแหน่งจำนวนเต็ม -1 รอบเป็นสิบที่ใกล้ที่สุด -2 รอบเป็นร้อยที่ใกล้ที่สุด และ 0 รอบเป็นจำนวนเต็ม (ตำแหน่งแรก) แต่คืนค่าประเภททศนิยม ไม่เหมือนเมื่อละเว้น

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

ปัดเศษจำนวนเต็มเป็นจำนวนหลักเท่าใดก็ได้

ต่อไปนี้คือตัวอย่างการประมวลผลสำหรับชนิด integer int

ถ้าอาร์กิวเมนต์ที่สองถูกละเว้น หรือถ้าระบุ 0 หรือจำนวนเต็มบวก ค่าเดิมจะถูกส่งคืนตามที่เป็นอยู่ หากระบุจำนวนเต็มลบ จะมีการปัดเศษเป็นเลขจำนวนเต็มที่เกี่ยวข้อง ในทั้งสองกรณี มีการส่งคืนชนิด 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() ปัดเศษเป็นเลขคู่ ไม่ใช่การปัดเศษทั่วไป

โปรดทราบว่าการปัดเศษด้วยฟังก์ชัน round() ในตัวใน Python 3 จะปัดเศษเป็นเลขคู่ ไม่ใช่การปัดเศษทั่วไป

ตามที่เขียนไว้ในเอกสารอย่างเป็นทางการ 0.5 จะถูกปัดเศษเป็น 0, 5 ถูกปัดเศษเป็น 0 เป็นต้น

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

คำจำกัดความของการปัดเศษเป็นเลขคู่มีดังนี้

หากเศษส่วนน้อยกว่า 0.5 ให้ปัดเศษลง ถ้าเศษส่วนมากกว่า 0.5 ให้ปัดขึ้น ถ้าเศษส่วนเป็น 0.5 พอดี ให้ปัดขึ้นเป็นเลขคู่ระหว่างการปัดเศษลงกับการปัดขึ้น
Rounding – Wikipedia

0.5 ไม่ถูกตัดทอนเสมอไป

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

ในบางกรณี คำจำกัดความของการปัดเศษเป็นเลขคู่ใช้ไม่ได้กับการประมวลผลหลังจากทศนิยมสองตำแหน่ง

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

นี่เป็นเพราะความจริงที่ว่าทศนิยมไม่สามารถแสดงเป็นตัวเลขทศนิยมได้ตรงตามที่ระบุไว้ในเอกสารอย่างเป็นทางการ

พฤติกรรมของ round() สำหรับตัวเลขทศนิยมอาจทำให้คุณประหลาดใจ:ตัวอย่างเช่น round(2.675, 2) จะให้ 2.67 แทนที่จะเป็น 2.68 ตามที่คาดไว้ นี่ไม่ใช่ข้อผิดพลาด:นี่เป็นผลมาจากข้อเท็จจริงที่ว่าทศนิยมส่วนใหญ่ไม่สามารถแสดงด้วยตัวเลขทศนิยมได้อย่างแม่นยำ
round() — Built-in Functions — Python 3.10.2 Documentation

ถ้าคุณต้องการให้การปัดเศษทั่วไปหรือการปัดเศษทศนิยมเป็นจำนวนคู่อย่างแม่นยำ คุณสามารถใช้ควอนไทซ์ทศนิยมของไลบรารีมาตรฐาน (อธิบายไว้ด้านล่าง) หรือกำหนดฟังก์ชันใหม่

โปรดทราบด้วยว่า round() ใน Python 2 ไม่ได้ปัดเศษเป็นเลขคู่ แต่เป็นการปัดเศษ

quantize() ของทศนิยมไลบรารีมาตรฐาน

โมดูลทศนิยมของไลบรารีมาตรฐานสามารถใช้เพื่อจัดการกับตัวเลขทศนิยมที่แน่นอนได้

การใช้เมธอด quantize() ของโมดูลทศนิยม เป็นไปได้ที่จะปัดเศษตัวเลขโดยระบุโหมดการปัดเศษ

ค่าที่ตั้งไว้สำหรับการปัดเศษอาร์กิวเมนต์ของเมธอด quantize() มีความหมายดังต่อไปนี้ ตามลำดับ

  • ROUND_HALF_UP:การปัดเศษทั่วไป
  • ROUND_HALF_EVEN:การปัดเศษเป็นเลขคู่

โมดูลทศนิยมเป็นไลบรารีมาตรฐาน ดังนั้นจึงไม่จำเป็นต้องติดตั้งเพิ่มเติม แต่จำเป็นต้องนำเข้า

from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN

การสร้างวัตถุทศนิยม

Decimal() สามารถใช้เพื่อสร้างวัตถุประเภท Decimal

หากคุณระบุประเภททศนิยมเป็นอาร์กิวเมนต์ คุณสามารถดูได้ว่าค่าใดที่ได้รับการปฏิบัติจริง

print(Decimal(0.05))
# 0.05000000000000000277555756156289135105907917022705078125

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

ดังที่แสดงในตัวอย่าง 0.05 ไม่ถือเป็น 0.05 ทุกประการ นี่คือสาเหตุที่ฟังก์ชัน round() ในตัวที่อธิบายข้างต้นถูกปัดเศษเป็นค่าที่ต่างจากที่คาดไว้สำหรับค่าทศนิยม ซึ่งรวมถึง 0.05 ในตัวอย่าง

เนื่องจาก 0.5 คือครึ่งหนึ่ง (-1 ยกกำลัง 2) จึงสามารถแสดงเป็นเลขฐานสองได้อย่างแม่นยำ

print(Decimal(0.5))
# 0.5

หากคุณระบุประเภทสตริง str แทนที่จะเป็นประเภททศนิยม จะถือว่าเป็นประเภททศนิยมของค่าที่แน่นอน

print(Decimal('0.05'))
# 0.05

การปัดเศษทศนิยมเป็นจำนวนหลักใด ๆ และการปัดเศษเป็นเลขคู่

เรียก quantize() จากวัตถุประเภททศนิยมเพื่อปัดเศษค่า

อาร์กิวเมนต์แรกของ quantize() คือสตริงที่มีจำนวนหลักเท่ากับจำนวนหลักที่คุณต้องการค้นหา เช่น ‘0.1’ หรือ ‘0.01’

นอกจากนี้ อาร์กิวเมนต์ ROUNDING ยังระบุโหมดการปัดเศษ หากระบุ ROUND_HALF_UP ระบบจะใช้การปัดเศษทั่วไป

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

ต่างจากฟังก์ชันในตัว round(), 0.5 ถูกปัดเศษเป็น 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

ถ้าการปัดเศษอาร์กิวเมนต์ถูกตั้งค่าเป็น ROUND_HALF_EVEN การปัดเศษจะดำเนินการเป็นเลขคู่เช่นเดียวกับในฟังก์ชันที่มีอยู่แล้วภายใน round()

ดังที่กล่าวไว้ข้างต้น หากระบุประเภททศนิยมของทศนิยมเป็นอาร์กิวเมนต์ของ Decimal() จะถือว่าเป็นอ็อบเจ็กต์ทศนิยมที่มีค่าเท่ากับมูลค่าจริงของประเภท float ดังนั้นผลลัพธ์ของการใช้ quantize() เมธอดจะแตกต่างไปจากที่คาดไว้ เช่นเดียวกับฟังก์ชัน 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

หากอาร์กิวเมนต์ของ Decimal() ถูกระบุเป็นสตริงประเภท str จะถือว่าเป็นอ็อบเจ็กต์ทศนิยมของค่านั้น ดังนั้นผลลัพธ์จะเป็นตามที่คาดไว้

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 สามารถจัดการได้อย่างถูกต้องโดยประเภท float จึงไม่มีปัญหาในการระบุประเภท float เป็นอาร์กิวเมนต์ของ Decimal() เมื่อปัดเศษเป็นจำนวนเต็ม แต่จะปลอดภัยกว่าที่จะระบุประเภทสตริง str เมื่อปัดเศษเป็นทศนิยม

ตัวอย่างเช่น 2.675 จริง ๆ แล้ว 2.67499…. ในประเภททุ่น ดังนั้น หากคุณต้องการปัดเศษทศนิยมสองตำแหน่ง คุณต้องระบุสตริงเป็น Decimal() มิฉะนั้น ผลลัพธ์จะแตกต่างจากผลลัพธ์ที่คาดไว้ ไม่ว่าคุณจะปัดเศษเป็นจำนวนเต็มที่ใกล้เคียงที่สุด (ROUND_HALF_UP) หรือเป็นเลขคู่ (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

โปรดทราบว่าเมธอด quantize() จะคืนค่าตัวเลขประเภททศนิยม ดังนั้นหากคุณต้องการใช้งานหมายเลขประเภททศนิยม คุณต้องแปลงเป็นประเภททศนิยมโดยใช้ float() ไม่เช่นนั้นจะเกิดข้อผิดพลาดขึ้น

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

การปัดเศษจำนวนเต็มเป็นจำนวนหลักใดๆ และการปัดเศษเป็นเลขคู่

หากคุณต้องการปัดเศษเป็นเลขจำนวนเต็ม การระบุบางอย่างเช่น ’10’ เนื่องจากอาร์กิวเมนต์แรกจะไม่ให้ผลลัพธ์ที่ต้องการ

i = 99518

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

นี่เป็นเพราะ quantize() ทำการปัดเศษตามเลขชี้กำลังของวัตถุทศนิยม แต่เลขชี้กำลังของ Decimal(’10’) คือ 0 ไม่ใช่ 1

คุณสามารถระบุเลขชี้กำลังตามอำเภอใจได้โดยใช้ E เป็นสตริงเลขชี้กำลัง (เช่น ‘1E1’) สามารถตรวจสอบเลขชี้กำลังได้ในวิธี 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)

ตามที่เป็นอยู่ ผลลัพธ์จะอยู่ในรูปแบบเลขชี้กำลังโดยใช้ E หากคุณต้องการใช้สัญกรณ์ปกติ หรือหากคุณต้องการทำงานกับชนิด integer int หลังจากปัดเศษ ให้ใช้ int() เพื่อแปลงผลลัพธ์

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

หากตั้งค่าการปัดเศษอาร์กิวเมนต์เป็น ROUND_HALF_UP การปัดเศษทั่วไปจะเกิดขึ้น เช่น 5 จะถูกปัดเศษเป็น 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

แน่นอนว่าไม่มีปัญหาหากคุณระบุเป็นสตริง

กำหนดฟังก์ชันใหม่

วิธีการใช้โมดูลทศนิยมนั้นแม่นยำและปลอดภัย แต่ถ้าคุณไม่สะดวกใจกับการแปลงประเภท คุณสามารถกำหนดฟังก์ชันใหม่เพื่อให้เกิดการปัดเศษทั่วไปได้

มีหลายวิธีในการทำเช่นนี้ ตัวอย่างเช่น ฟังก์ชันต่อไปนี้

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

หากคุณไม่ต้องการระบุจำนวนหลักและปัดเศษเป็นทศนิยมแรกเสมอ คุณสามารถใช้แบบฟอร์มที่ง่ายกว่า

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

หากคุณต้องการความแม่นยำ การใช้ทศนิยมจะปลอดภัยกว่า

ต่อไปนี้สำหรับการอ้างอิงเท่านั้น

ปัดเศษทศนิยมเป็นจำนวนหลักเท่าใดก็ได้

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

ต่างจากรอบ 0.5 กลายเป็น 1 ตามการปัดเศษทั่วไป

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

ปัดเศษจำนวนเต็มเป็นจำนวนหลัก

i = 99518

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

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

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

ต่างจากรอบ 5 กลายเป็น 10 ตามการปัดเศษทั่วไป

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

หมายเหตุ: สำหรับค่าลบ

ในฟังก์ชันตัวอย่างด้านบน -0.5 จะถูกปัดเศษเป็น 0

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

มีหลายวิธีในการคิดเกี่ยวกับการปัดเศษค่าลบ แต่ถ้าคุณต้องการทำให้ -0.5 เป็น -1 คุณสามารถแก้ไขได้ดังนี้

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