ฉันต้องการลบตัวเลขออกจากทศนิยมเพื่อให้มีจำนวนหลักคงที่หลังจุดเช่น:
1.923328437452 -> 1.923
ฉันต้องการส่งออกเป็นสตริงไปยังฟังก์ชันอื่นไม่ใช่พิมพ์
นอกจากนี้ฉันต้องการละเว้นตัวเลขที่หายไปไม่ใช่ปัดเศษ
ฉันต้องการลบตัวเลขออกจากทศนิยมเพื่อให้มีจำนวนหลักคงที่หลังจุดเช่น:
1.923328437452 -> 1.923
ฉันต้องการส่งออกเป็นสตริงไปยังฟังก์ชันอื่นไม่ใช่พิมพ์
นอกจากนี้ฉันต้องการละเว้นตัวเลขที่หายไปไม่ใช่ปัดเศษ
คำตอบ:
ขั้นแรกฟังก์ชั่นสำหรับผู้ที่ต้องการเพียงแค่คัดลอกและวางโค้ด:
def truncate(f, n):
'''Truncates/pads a float f to n decimal places without rounding'''
s = '{}'.format(f)
if 'e' in s or 'E' in s:
return '{0:.{1}f}'.format(f, n)
i, p, d = s.partition('.')
return '.'.join([i, (d+'0'*n)[:n]])
ใช้ได้ใน Python 2.7 และ 3.1+ สำหรับเวอร์ชันเก่าจะไม่สามารถรับเอฟเฟกต์ "การปัดเศษอัจฉริยะ" แบบเดียวกันได้ (อย่างน้อยก็ไม่ใช่โดยไม่มีรหัสที่ซับซ้อนมากนัก) แต่การปัดเศษเป็นทศนิยม 12 ตำแหน่งก่อนการตัดทอนจะใช้เวลาส่วนใหญ่:
def truncate(f, n):
'''Truncates/pads a float f to n decimal places without rounding'''
s = '%.12f' % f
i, p, d = s.partition('.')
return '.'.join([i, (d+'0'*n)[:n]])
หลักของวิธีการพื้นฐานคือการแปลงค่าเป็นสตริงด้วยความแม่นยำสูงสุดจากนั้นเพียงแค่ตัดทุกอย่างที่เกินจำนวนอักขระที่ต้องการ ขั้นตอนหลังทำได้ง่าย สามารถทำได้ด้วยการจัดการสตริง
i, p, d = s.partition('.')
'.'.join([i, (d+'0'*n)[:n]])
หรือdecimal
โมดูล
str(Decimal(s).quantize(Decimal((0, (1,), -n)), rounding=ROUND_DOWN))
ขั้นตอนแรกการแปลงเป็นสตริงนั้นค่อนข้างยากเนื่องจากมีลิเทอรัลจุดลอยตัวบางคู่ (เช่นสิ่งที่คุณเขียนในซอร์สโค้ด) ซึ่งทั้งคู่ให้การแทนค่าไบนารีเหมือนกัน แต่ก็ควรจะถูกตัดทอนให้แตกต่างกัน ตัวอย่างเช่นพิจารณา 0.3 และ 0.29999999999999998 หากคุณเขียน0.3
ในโปรแกรม Python คอมไพลเลอร์จะเข้ารหัสโดยใช้รูปแบบทศนิยมของ IEEE ในลำดับของบิต (สมมติว่าเป็นทศนิยม 64 บิต)
0011111111010011001100110011001100110011001100110011001100110011
นี่คือค่าที่ใกล้เคียงที่สุดกับ 0.3 ที่สามารถแสดงเป็นค่าลอยของ IEEE ได้อย่างแม่นยำ แต่ถ้าคุณเขียน0.29999999999999998
ด้วยโปรแกรม Python คอมไพลเลอร์จะแปลเป็นค่าเดียวกันทั้งหมด ในกรณีหนึ่งคุณตั้งใจให้มันถูกตัดทอน (เป็นหนึ่งหลัก) ใน0.3
ขณะที่อีกกรณีหนึ่งคุณหมายถึงมันถูกตัดให้สั้นลง0.2
แต่ Python สามารถให้คำตอบได้เพียงคำตอบเดียว นี่เป็นข้อ จำกัด พื้นฐานของ Python หรือภาษาโปรแกรมใด ๆ โดยไม่ต้องใช้การประเมิน ฟังก์ชันการตัดทอนจะเข้าถึงเฉพาะค่าไบนารีที่จัดเก็บไว้ในหน่วยความจำของคอมพิวเตอร์ไม่ใช่สตริงที่คุณพิมพ์ลงในซอร์สโค้ดจริงๆ 1
หากคุณถอดรหัสลำดับของบิตกลับเป็นเลขฐานสิบอีกครั้งโดยใช้รูปแบบทศนิยม 64 บิตของ IEEE คุณจะได้รับ
0.2999999999999999888977697537484345957637...
ดังนั้นการนำไปใช้อย่างไร้เดียงสาจะเกิดขึ้น0.2
แม้ว่านั่นอาจไม่ใช่สิ่งที่คุณต้องการก็ตาม สำหรับข้อมูลเพิ่มเติมเกี่ยวลอยจุดผิดพลาดแทนดูงูหลามกวดวิชา
เป็นเรื่องยากมากที่จะทำงานกับค่าทศนิยมที่ใกล้เคียงกับตัวเลขกลมๆ แต่ตั้งใจจะไม่เท่ากับจำนวนรอบนั้น ดังนั้นเมื่อตัดทอนจึงเป็นเรื่องที่สมเหตุสมผลที่จะเลือกการแทนค่าทศนิยม "ที่ดีที่สุด" จากทั้งหมดที่สามารถสอดคล้องกับค่าในหน่วยความจำ Python 2.7 ขึ้นไป (แต่ไม่ใช่ 3.0) มีอัลกอริทึมที่ซับซ้อนเพื่อทำสิ่งนั้นซึ่งเราสามารถเข้าถึงได้ผ่านการดำเนินการจัดรูปแบบสตริงเริ่มต้น
'{}'.format(f)
ข้อแม้เดียวคือสิ่งนี้ทำหน้าที่เหมือนg
ข้อกำหนดรูปแบบในแง่ที่ใช้สัญกรณ์เอกซ์โพเนนเชียล ( 1.23e+4
) ถ้าตัวเลขมีขนาดใหญ่หรือเล็กพอ ดังนั้นวิธีการต้องจับกรณีนี้และจัดการมันแตกต่างกัน มีบางกรณีที่การใช้f
ข้อกำหนดรูปแบบแทนทำให้เกิดปัญหาเช่นการพยายามตัดทอน3e-10
ความแม่นยำให้เหลือ 28 หลัก (ซึ่งเกิดขึ้น0.0000000002999999999999999980
) และฉันยังไม่แน่ใจว่าจะจัดการกับสิ่งเหล่านั้นได้ดีที่สุดอย่างไร
หากคุณกำลังทำงานกับfloat
s ที่มีค่าใกล้เคียงกับตัวเลขกลมๆมาก แต่ไม่ได้ตั้งใจให้เท่ากับจำนวนนั้น (เช่น 0.29999999999999998 หรือ 99.959999999999994) สิ่งนี้จะทำให้เกิดผลบวกลวงกล่าวคือจะปัดเศษตัวเลขที่คุณไม่ต้องการให้ปัดเศษ ในกรณีนี้วิธีแก้ปัญหาคือการระบุความแม่นยำคงที่
'{0:.{1}f}'.format(f, sys.float_info.dig + n + 2)
จำนวนหลักของความแม่นยำที่จะใช้ในที่นี้ไม่สำคัญนักเพียง แต่ต้องมีขนาดใหญ่พอที่จะทำให้แน่ใจได้ว่าการปัดเศษใด ๆ ที่ดำเนินการในการแปลงสตริงจะไม่ "ชน" ค่าเป็นการแทนทศนิยมที่ดี ฉันคิดว่าsys.float_info.dig + n + 2
อาจจะเพียงพอในทุกกรณี แต่ถ้าไม่เช่นนั้น2
อาจจะต้องเพิ่มขึ้นและไม่เจ็บที่จะทำเช่นนั้น
ใน Python เวอร์ชันก่อนหน้า (มากถึง 2.6 หรือ 3.0) การจัดรูปแบบตัวเลขทศนิยมนั้นค่อนข้างหยาบกว่ามากและมักจะสร้างสิ่งต่างๆเช่น
>>> 1.1
1.1000000000000001
หากนี่คือสถานการณ์ของคุณหากคุณไม่ต้องการใช้การแทนค่าทศนิยม "nice" สำหรับการตัดทอนสิ่งที่คุณทำได้ (เท่าที่ฉันรู้) คือเลือกตัวเลขจำนวนหนึ่งน้อยกว่าความแม่นยำเต็มที่แทนค่าได้ด้วย a float
และปัดเศษ ให้เป็นตัวเลขจำนวนมากก่อนที่จะตัดทอน ตัวเลือกทั่วไปคือ 12
'%.12f' % f
แต่คุณสามารถปรับให้เหมาะกับตัวเลขที่คุณใช้
1อืม ... ฉันโกหก ในทางเทคนิคคุณสามารถสั่งให้ Python แยกวิเคราะห์ซอร์สโค้ดของตัวเองอีกครั้งและแยกส่วนที่เกี่ยวข้องกับอาร์กิวเมนต์แรกที่คุณส่งผ่านไปยังฟังก์ชันการตัดทอน หากอาร์กิวเมนต์นั้นเป็นลิเทอรัลทศนิยมคุณก็สามารถตัดตำแหน่งจำนวนหนึ่งออกหลังจากจุดทศนิยมแล้วส่งกลับ อย่างไรก็ตามกลยุทธ์นี้ใช้ไม่ได้หากอาร์กิวเมนต์เป็นตัวแปรซึ่งทำให้ไม่มีประโยชน์ รายการต่อไปนี้นำเสนอเพื่อความบันเทิงเท่านั้น:
def trunc_introspect(f, n):
'''Truncates/pads the float f to n decimal places by looking at the caller's source code'''
current_frame = None
caller_frame = None
s = inspect.stack()
try:
current_frame = s[0]
caller_frame = s[1]
gen = tokenize.tokenize(io.BytesIO(caller_frame[4][caller_frame[5]].encode('utf-8')).readline)
for token_type, token_string, _, _, _ in gen:
if token_type == tokenize.NAME and token_string == current_frame[3]:
next(gen) # left parenthesis
token_type, token_string, _, _, _ = next(gen) # float literal
if token_type == tokenize.NUMBER:
try:
cut_point = token_string.index('.') + n + 1
except ValueError: # no decimal in string
return token_string + '.' + '0' * n
else:
if len(token_string) < cut_point:
token_string += '0' * (cut_point - len(token_string))
return token_string[:cut_point]
else:
raise ValueError('Unable to find floating-point literal (this probably means you called {} with a variable)'.format(current_frame[3]))
break
finally:
del s, current_frame, caller_frame
การสรุปสิ่งนี้เพื่อจัดการกรณีที่คุณส่งผ่านตัวแปรดูเหมือนจะเป็นสาเหตุที่หายไปเนื่องจากคุณจะต้องติดตามย้อนกลับผ่านการดำเนินการของโปรแกรมจนกว่าคุณจะพบลิเทอรัลทศนิยมที่ให้ค่าตัวแปร ถ้ามีแม้แต่อันเดียว ตัวแปรส่วนใหญ่จะเริ่มต้นจากอินพุตของผู้ใช้หรือนิพจน์ทางคณิตศาสตร์ซึ่งในกรณีนี้การแทนค่าไบนารีก็มีอยู่ทั้งหมด
applymap()
) อาจมีวิธีที่จะทำให้การดำเนินการทั้งหมดมีประสิทธิภาพมากขึ้น แต่นั่นก็เป็นเรื่องสำหรับคำถามแยกต่างหาก
round(1.923328437452, 3)
ดูเอกสารงูใหญ่เกี่ยวกับประเภทมาตรฐาน คุณจะต้องเลื่อนลงเล็กน้อยเพื่อไปที่ฟังก์ชัน round โดยพื้นฐานแล้วตัวเลขที่สองจะบอกจำนวนทศนิยมที่ต้องปัดเศษ
ผลลัพธ์ของround
การลอยตัวดังนั้นโปรดระวัง (ตัวอย่างมาจาก Python 2.6):
>>> round(1.923328437452, 3)
1.923
>>> round(1.23456, 3)
1.2350000000000001
คุณจะดีขึ้นเมื่อใช้สตริงที่จัดรูปแบบ:
>>> "%.3f" % 1.923328437452
'1.923'
>>> "%.3f" % 1.23456
'1.235'
round(1.23456, 3)
is 1.235
and not1.2350000000000001
n = 1.923328437452
str(n)[:4]
2
คุณจะมีจุดทศนิยม.
ที่ท้ายสตริงซึ่งไม่ใช่วิธีแก้ปัญหาที่ดีจริงๆ
ที่พรอมต์ Python 2.7 ของฉัน:
>>> int(1.923328437452 * 1000)/1000.0
1.923
สคริปต์ python อย่างง่าย -
n = 1.923328437452
n = float(int(n * 1000))
n /=1000
def trunc(num, digits):
sp = str(num).split('.')
return '.'.join([sp[0], sp[1][:digits]])
สิ่งนี้ควรใช้งานได้ ควรให้การตัดทอนที่คุณกำลังมองหา
วิธีการทำแบบไพ ธ อนิกที่แท้จริงคือ
from decimal import *
with localcontext() as ctx:
ctx.rounding = ROUND_DOWN
print Decimal('1.923328437452').quantize(Decimal('0.001'))
หรือสั้นกว่า:
from decimal import Decimal as D, ROUND_DOWN
D('1.923328437452').quantize(D('0.001'), rounding=ROUND_DOWN)
อัปเดต
โดยปกติแล้วปัญหาไม่ได้อยู่ที่การตัดการลอยตัวเอง แต่เกิดจากการใช้ตัวเลขลูกลอยที่ไม่เหมาะสมก่อนการปัดเศษ
ตัวอย่างเช่น: int(0.7*3*100)/100 == 2.09
.
หากคุณถูกบังคับให้ใช้โฟลท (เช่นคุณกำลังเร่งโค้ดของคุณด้วยnumba
) ควรใช้เซ็นต์เป็น "การนำเสนอภายใน" ของราคา: ( 70*3 == 210
) และคูณ / หารอินพุต / เอาต์พุต
int(0.7*3*100)/100 == 2.09
. เงิน 1 เซ็นต์ของฉันหายไปไหน
ImportError: cannot import name 'D'
ฉันเชื่อว่าคุณต้องการสร้างชื่อนำเข้าไม่?
คำตอบจำนวนมากที่ได้รับสำหรับคำถามนี้ผิดอย่างสิ้นเชิง พวกเขาปัดเศษขึ้น (แทนที่จะตัดทอน) หรือไม่ได้ผลในทุกกรณี
นี่เป็นผลการค้นหาอันดับต้น ๆ ของ Google เมื่อฉันค้นหา 'Python truncate float' ซึ่งเป็นแนวคิดที่ตรงไปตรงมามากและสมควรได้รับคำตอบที่ดีกว่า ฉันเห็นด้วยกับ Hatchkins ว่าการใช้decimal
โมดูลเป็นวิธีการทำแบบ pythonic ดังนั้นฉันจึงให้ฟังก์ชั่นที่ฉันคิดว่าตอบคำถามได้ถูกต้องและใช้ได้ตามที่คาดไว้สำหรับทุกกรณี
โดยทั่วไปแล้วค่าเศษส่วนจะไม่สามารถแสดงได้อย่างแน่นอนโดยตัวแปรจุดลอยตัวไบนารี (ดูที่นี่สำหรับการอภิปรายเกี่ยวกับเรื่องนี้) ซึ่งเป็นสาเหตุที่ฟังก์ชันของฉันส่งคืนสตริง
from decimal import Decimal, localcontext, ROUND_DOWN
def truncate(number, places):
if not isinstance(places, int):
raise ValueError("Decimal places must be an integer.")
if places < 1:
raise ValueError("Decimal places must be at least 1.")
# If you want to truncate to 0 decimal places, just do int(number).
with localcontext() as context:
context.rounding = ROUND_DOWN
exponent = Decimal(str(10 ** - places))
return Decimal(str(number)).quantize(exponent).to_eng_string()
ฉันทำสิ่งนี้:
from math import trunc
def truncate(number, decimals=0):
if decimals < 0:
raise ValueError('truncate received an invalid value of decimals ({})'.format(decimals))
elif decimals == 0:
return trunc(number)
else:
factor = float(10**decimals)
return trunc(number*factor)/factor
คุณทำได้:
def truncate(f, n):
return math.floor(f * 10 ** n) / 10 ** n
การทดสอบ:
>>> f=1.923328437452
>>> [truncate(f, n) for n in range(5)]
[1.0, 1.9, 1.92, 1.923, 1.9233]
หากคุณชอบคณิตศาสตร์สิ่งนี้ใช้ได้กับตัวเลข + ve:
>>> v = 1.923328437452
>>> v - v % 1e-3
1.923
เมื่อใช้แพนด้า df สิ่งนี้ได้ผลสำหรับฉัน
import math
def truncate(number, digits) -> float:
stepper = 10.0 ** digits
return math.trunc(stepper * number) / stepper
df['trunc'] = df['float_val'].apply(lambda x: truncate(x,1))
df['trunc']=df['trunc'].map('{:.1f}'.format)
แค่อยากจะบอกว่าเคล็ดลับ "make round () with floor ()" แบบเก่าของ
round(f) = floor(f+0.5)
สามารถหมุนไปรอบ ๆ เพื่อสร้างพื้น () จากรอบ ()
floor(f) = round(f-0.5)
แม้ว่ากฎทั้งสองนี้จะทำลายจำนวนลบดังนั้นการใช้จึงน้อยกว่าอุดมคติ:
def trunc(f, n):
if f > 0:
return "%.*f" % (n, (f - 0.5*10**-n))
elif f == 0:
return "%.*f" % (n, f)
elif f < 0:
return "%.*f" % (n, (f + 0.5*10**-n))
int (16.5); สิ่งนี้จะให้ค่าจำนวนเต็ม 16 เช่น trunc จะไม่สามารถระบุทศนิยมได้ แต่เดาว่าคุณสามารถทำได้โดย
import math;
def trunc(invalue, digits):
return int(invalue*math.pow(10,digits))/math.pow(10,digits);
นี่คือวิธีง่ายๆ:
def truncate(num, res=3):
return (floor(num*pow(10, res)+0.5))/pow(10, res)
สำหรับ num = 1.923328437452 เอาต์พุตนี้ 1.923
def trunc(f,n):
return ('%.16f' % f)[:(n-16)]
ฟังก์ชันทั่วไปและใช้งานง่าย:
def truncate_float(number, length):
"""Truncate float numbers, up to the number specified
in length that must be an integer"""
number = number * pow(10, length)
number = int(number)
number = float(number)
number /= pow(10, length)
return number
มีวิธีแก้ปัญหาง่ายๆใน python 3 ที่ที่จะตัดฉันกำหนดด้วยตัวแปรความช่วยเหลือ decPlace เพื่อให้ปรับตัวได้ง่าย
f = 1.12345
decPlace= 4
f_cut = int(f * 10**decPlace) /10**decPlace
เอาท์พุต:
f = 1.1234
หวังว่าจะช่วยได้
def precision(value, precision):
"""
param: value: takes a float
param: precision: int, number of decimal places
returns a float
"""
x = 10.0**precision
num = int(value * x)/ x
return num
precision(1.923328437452, 3)
1.923
ตัวแปรที่สั้นและง่าย
def truncate_float(value, digits_after_point=2):
pow_10 = 10 ** digits_after_point
return (float(int(value * pow_10))) / pow_10
>>> truncate_float(1.14333, 2)
>>> 1.14
>>> truncate_float(1.14777, 2)
>>> 1.14
>>> truncate_float(1.14777, 4)
>>> 1.1477
คำตอบส่วนใหญ่ซับซ้อนเกินไปในความคิดของฉันแล้วสิ่งนี้ล่ะ?
digits = 2 # Specify how many digits you want
fnum = '122.485221'
truncated_float = float(fnum[:fnum.find('.') + digits + 1])
>>> 122.48
เพียงแค่สแกนหาดัชนีของ "." และตัดทอนตามต้องการ (ไม่มีการปัดเศษ) แปลงสตริงให้ลอยเป็นขั้นตอนสุดท้าย
หรือในกรณีของคุณถ้าคุณได้ float เป็นอินพุตและต้องการสตริงเป็นเอาต์พุต:
fnum = str(122.485221) # convert float to string first
truncated_float = fnum[:fnum.find('.') + digits + 1] # string output
>>> floor((1.23658945) * 10**4) / 10**4
1.2365
# หารและคูณด้วย 10 ** จำนวนหลักที่ต้องการ
ใช้ numpy.round
import numpy as np
precision = 3
floats = [1.123123123, 2.321321321321]
new_float = np.round(floats, precision)
สิ่งที่ง่ายพอที่จะทำให้เข้าใจในรายการโดยไม่มีไลบรารีหรือการอ้างอิงภายนอกอื่น ๆ สำหรับ Python> = 3.6 การเขียนด้วย f-strings นั้นง่ายมาก
แนวคิดคือปล่อยให้การแปลงสตริงทำการปัดเศษไปยังอีกที่หนึ่งมากกว่าที่คุณต้องการแล้วตัดตัวเลขสุดท้ายออก
>>> nout = 3 # desired number of digits in output
>>> [f'{x:.{nout+1}f}'[:-1] for x in [2/3, 4/5, 8/9, 9/8, 5/4, 3/2]]
['0.666', '0.800', '0.888', '1.125', '1.250', '1.500']
แน่นอนว่ายังมีการปัดเศษเกิดขึ้นที่นี่ (คือสำหรับหลักที่สี่) แต่การปัดเศษในบางจุดเป็น unvoidable ในกรณีที่การเปลี่ยนระหว่างการตัดทอนและการปัดเศษมีความเกี่ยวข้องนี่คือตัวอย่างที่ดีกว่าเล็กน้อย:
>>> nacc = 6 # desired accuracy (maximum 15!)
>>> nout = 3 # desired number of digits in output
>>> [f'{x:.{nacc}f}'[:-(nacc-nout)] for x in [2.9999, 2.99999, 2.999999, 2.9999999]]
>>> ['2.999', '2.999', '2.999', '3.000']
โบนัส: ลบศูนย์ทางด้านขวา
>>> nout = 3 # desired number of digits in output
>>> [f'{x:.{nout+1}f}'[:-1].rstrip('0') for x in [2/3, 4/5, 8/9, 9/8, 5/4, 3/2]]
['0.666', '0.8', '0.888', '1.125', '1.25', '1.5']
แนวคิดหลักที่ให้ไว้ในที่นี้ดูเหมือนว่าฉันจะเป็นแนวทางที่ดีที่สุดสำหรับปัญหานี้ น่าเสียดายที่ได้รับการโหวตน้อยลงในขณะที่คำตอบในภายหลังที่มีคะแนนโหวตมากกว่านั้นยังไม่สมบูรณ์ (ตามที่สังเกตในความคิดเห็น) หวังว่าการดำเนินการดังต่อไปนี้ให้สั้นและโซลูชั่นที่สมบูรณ์สำหรับการตัด
def trunc(num, digits):
l = str(float(num)).split('.')
digits = min(len(l[1]), digits)
return (l[0]+'.'+l[1][:digits])
ซึ่งควรดูแลกรณีมุมทั้งหมดที่พบที่นี่และที่นี่
ฉันยังเป็นมือใหม่หัดงูและหลังจากใช้ประโยชน์จากบิตและชิ้นส่วนที่นี่ฉันขอเสนอสองเซ็นต์ของฉัน
print str(int(time.time()))+str(datetime.now().microsecond)[:3]
str (int (time.time ())) จะใช้เวลา epoch เป็น int และแปลงเป็นสตริงและเข้าร่วมกับ ... str (datetime.now (). microsecond) [: 3] ซึ่งส่งคืนไมโครวินาทีเท่านั้นแปลง เพื่อสตริงและตัดทอนเป็น 3 ตัวอักษรแรก
# value value to be truncated
# n number of values after decimal
value = 0.999782
n = 3
float(int(value*1en))*1e-n
หากคุณหมายถึงเมื่อพิมพ์สิ่งต่อไปนี้ควรใช้งานได้:
print '%.3f' % number