Tôi muốn xóa các chữ số khỏi số float để có một số lượng chữ số cố định sau dấu chấm, như:
1.923328437452 -> 1.923
Tôi cần xuất dưới dạng một chuỗi cho một hàm khác, không phải in.
Ngoài ra tôi muốn bỏ qua các chữ số bị mất, không làm tròn chúng.
Tôi muốn xóa các chữ số khỏi số float để có một số lượng chữ số cố định sau dấu chấm, như:
1.923328437452 -> 1.923
Tôi cần xuất dưới dạng một chuỗi cho một hàm khác, không phải in.
Ngoài ra tôi muốn bỏ qua các chữ số bị mất, không làm tròn chúng.
Câu trả lời:
Đầu tiên, chức năng dành cho những người chỉ muốn sao chép và dán mã:
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]])
Điều này hợp lệ trong Python 2.7 và 3.1+. Đối với các phiên bản cũ hơn, không thể có được hiệu ứng "làm tròn thông minh" tương tự (ít nhất, không phải là không có nhiều mã phức tạp), nhưng việc làm tròn đến 12 chữ số thập phân trước khi cắt ngắn sẽ hiệu quả trong phần lớn thời gian:
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]])
Cốt lõi của phương pháp cơ bản là chuyển đổi giá trị thành một chuỗi với độ chính xác đầy đủ và sau đó chỉ cần cắt mọi thứ vượt quá số ký tự mong muốn. Bước sau rất dễ dàng; nó có thể được thực hiện với thao tác chuỗi
i, p, d = s.partition('.')
'.'.join([i, (d+'0'*n)[:n]])
hoặc decimalmô-đun
str(Decimal(s).quantize(Decimal((0, (1,), -n)), rounding=ROUND_DOWN))
Bước đầu tiên, chuyển đổi thành một chuỗi, khá khó khăn vì có một số cặp ký tự dấu phẩy động (tức là những gì bạn viết trong mã nguồn), cả hai đều tạo ra cùng một biểu diễn nhị phân nhưng nên được cắt ngắn khác nhau. Ví dụ: hãy xem xét 0,3 và 0,29999999999999998. Nếu bạn viết 0.3bằng chương trình Python, trình biên dịch sẽ mã hóa nó bằng cách sử dụng định dạng dấu phẩy động IEEE thành chuỗi các bit (giả sử là một số float 64 bit)
0011111111010011001100110011001100110011001100110011001100110011
Đây là giá trị gần nhất với 0,3 có thể được biểu diễn chính xác dưới dạng một IEEE float. Nhưng nếu bạn viết 0.29999999999999998bằng chương trình Python, trình biên dịch sẽ dịch nó thành chính xác cùng một giá trị . Trong một trường hợp, bạn có nghĩa là nó được cắt ngắn (thành một chữ số) 0.3, trong khi trong trường hợp khác, bạn muốn nó được cắt ngắn thành 0.2, nhưng Python chỉ có thể đưa ra một câu trả lời. Đây là một hạn chế cơ bản của Python, hoặc thực sự là bất kỳ ngôn ngữ lập trình nào mà không đánh giá lười biếng. Hàm cắt ngắn chỉ có quyền truy cập vào giá trị nhị phân được lưu trữ trong bộ nhớ của máy tính, không phải chuỗi bạn thực sự nhập vào mã nguồn. 1
Nếu bạn giải mã chuỗi bit trở lại thành số thập phân, một lần nữa bằng cách sử dụng định dạng dấu phẩy động 64 bit IEEE, bạn sẽ nhận được
0.2999999999999999888977697537484345957637...
vì vậy một triển khai ngây thơ sẽ xuất hiện 0.2mặc dù đó có thể không phải là điều bạn muốn. Để biết thêm về lỗi biểu diễn dấu phẩy động, hãy xem hướng dẫn Python .
Rất hiếm khi làm việc với một giá trị dấu phẩy động gần với một số tròn nhưng lại cố tình không bằng số tròn đó. Vì vậy, khi cắt ngắn, có lẽ hợp lý khi chọn biểu diễn thập phân "đẹp nhất" trong số tất cả những gì có thể tương ứng với giá trị trong bộ nhớ. Python 2.7 trở lên (nhưng không phải 3.0) bao gồm một thuật toán phức tạp để thực hiện điều đó , mà chúng ta có thể truy cập thông qua thao tác định dạng chuỗi mặc định.
'{}'.format(f)
Lưu ý duy nhất là điều này hoạt động giống như một gđặc tả định dạng, theo nghĩa là nó sử dụng ký hiệu hàm mũ ( 1.23e+4) nếu số lượng đủ lớn hoặc nhỏ. Vì vậy, phương pháp phải bắt trường hợp này và xử lý nó khác. Có một vài trường hợp mà việc sử dụng fđặc tả định dạng gây ra sự cố, chẳng hạn như cố gắng cắt bớt 3e-10độ chính xác còn 28 chữ số (nó tạo ra 0.0000000002999999999999999980) và tôi chưa chắc cách tốt nhất để xử lý chúng.
Nếu bạn thực sự đang làm việc với floatcác s rất gần với số làm tròn nhưng cố ý không bằng chúng (như 0,29999999999999998 hoặc 99,959999999999994), điều này sẽ tạo ra một số dương tính giả, tức là nó sẽ làm tròn số mà bạn không muốn làm tròn. Trong trường hợp đó, giải pháp là chỉ định độ chính xác cố định.
'{0:.{1}f}'.format(f, sys.float_info.dig + n + 2)
Số lượng chữ số chính xác để sử dụng ở đây không thực sự quan trọng, nó chỉ cần đủ lớn để đảm bảo rằng bất kỳ phép làm tròn nào được thực hiện trong chuyển đổi chuỗi không làm "tăng" giá trị lên biểu diễn thập phân đẹp mắt của nó. Tôi nghĩ sys.float_info.dig + n + 2có thể là đủ trong mọi trường hợp, nhưng nếu không thì 2có thể phải tăng lên, và làm như vậy cũng không hại gì.
Trong các phiên bản trước của Python (lên đến 2.6 hoặc 3.0), định dạng số dấu phẩy động thô hơn rất nhiều và sẽ thường xuyên tạo ra những thứ như
>>> 1.1
1.1000000000000001
Nếu đây là tình hình của bạn, nếu bạn làm muốn sử dụng "thoải mái" cơ quan đại diện thập phân cho cắt ngắn, tất cả các bạn có thể làm (như xa như tôi biết) là chọn một số số chữ số, thấp hơn biểu diễn chính xác đầy đủ bởi một floatvà vòng quanh đánh số đến nhiều chữ số đó trước khi cắt bớt. Một lựa chọn điển hình là 12,
'%.12f' % f
nhưng bạn có thể điều chỉnh điều này cho phù hợp với các số bạn đang sử dụng.
1 Chà ... tôi đã nói dối. Về mặt kỹ thuật, bạn có thể hướng dẫn Python phân tích lại mã nguồn của chính nó và trích xuất phần tương ứng với đối số đầu tiên mà bạn chuyển cho hàm cắt ngắn. Nếu đối số đó là một ký tự dấu phẩy động, bạn chỉ có thể cắt nó đi một số vị trí nhất định sau dấu thập phân và trả về. Tuy nhiên, chiến lược này không hoạt động nếu đối số là một biến, điều này khiến nó khá vô dụng. Những điều sau đây chỉ được trình bày cho giá trị giải trí:
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
Tổng quát hóa điều này để xử lý trường hợp bạn truyền vào một biến có vẻ như là một nguyên nhân bị mất, vì bạn phải truy ngược lại quá trình thực thi của chương trình cho đến khi bạn tìm thấy ký tự dấu phẩy động đã cho biến giá trị của nó. Nếu thậm chí có một. Hầu hết các biến sẽ được khởi tạo từ đầu vào của người dùng hoặc các biểu thức toán học, trong trường hợp đó, biểu diễn nhị phân là tất cả.
applymap()). Có thể có một cách để làm cho toàn bộ hoạt động hiệu quả hơn, nhưng đó sẽ là một vấn đề cho một câu hỏi riêng.
round(1.923328437452, 3)
Xem tài liệu của Python về các loại tiêu chuẩn . Bạn sẽ cần phải cuộn xuống một chút để đến chức năng vòng. Về cơ bản, số thứ hai cho biết có bao nhiêu chữ số thập phân để làm tròn nó thành.
Kết quả của roundlà một float, vì vậy hãy chú ý (ví dụ là từ Python 2.6):
>>> round(1.923328437452, 3)
1.923
>>> round(1.23456, 3)
1.2350000000000001
Bạn sẽ tốt hơn khi sử dụng một chuỗi được định dạng:
>>> "%.3f" % 1.923328437452
'1.923'
>>> "%.3f" % 1.23456
'1.235'
round(1.23456, 3)có 1.235và không1.2350000000000001
n = 1.923328437452
str(n)[:4]
2, bạn sẽ có một dấu chấm thập phân .ở cuối chuỗi - tôi nghĩ không thực sự là một giải pháp tốt.
Tập lệnh python đơn giản -
n = 1.923328437452
n = float(int(n * 1000))
n /=1000
Cách làm thật sự của loài trăn là
from decimal import *
with localcontext() as ctx:
ctx.rounding = ROUND_DOWN
print Decimal('1.923328437452').quantize(Decimal('0.001'))
hoặc ngắn hơn:
from decimal import Decimal as D, ROUND_DOWN
D('1.923328437452').quantize(D('0.001'), rounding=ROUND_DOWN)
Cập nhật
Thông thường, vấn đề không nằm ở việc cắt bớt float mà là do việc sử dụng không đúng các số float trước khi làm tròn.
Ví dụ: int(0.7*3*100)/100 == 2.09.
Nếu bạn buộc phải sử dụng float (giả sử bạn đang tăng tốc mã của mình với numba), tốt hơn nên sử dụng xu làm "đại diện nội bộ" của giá: ( 70*3 == 210) và nhân / chia các đầu vào / đầu ra.
int(0.7*3*100)/100 == 2.09. 1 xu của tôi đã đi đâu?
ImportError: cannot import name 'D', tôi tin rằng bạn muốn thực hiện một lần nhập có tên là không?
Vì vậy, nhiều câu trả lời được đưa ra cho câu hỏi này là hoàn toàn sai. Chúng hoặc làm tròn nổi (thay vì cắt ngắn) hoặc không hoạt động cho mọi trường hợp.
Đây là kết quả hàng đầu của Google khi tôi tìm kiếm 'Python truncate float', một khái niệm thực sự đơn giản và xứng đáng có câu trả lời tốt hơn. Tôi đồng ý với Hatchkins rằng việc sử dụng decimalmô-đun là cách thực hiện điều này, vì vậy tôi đưa ra ở đây một chức năng mà tôi nghĩ là trả lời câu hỏi một cách chính xác và hoạt động như mong đợi cho mọi trường hợp.
Lưu ý thêm, nói chung, các giá trị phân số không thể được biểu diễn chính xác bằng các biến dấu chấm động nhị phân (xem ở đây để thảo luận về điều này), đó là lý do tại sao hàm của tôi trả về một chuỗi.
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()
Tôi đã làm một cái gì đó như thế này:
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
Bạn có thể làm:
def truncate(f, n):
return math.floor(f * 10 ** n) / 10 ** n
thử nghiệm:
>>> f=1.923328437452
>>> [truncate(f, n) for n in range(5)]
[1.0, 1.9, 1.92, 1.923, 1.9233]
Khi sử dụng gấu trúc df điều này làm việc cho tôi
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)
Chỉ muốn đề cập rằng thủ thuật cũ "tạo vòng () với sàn ()"
round(f) = floor(f+0.5)
có thể được xoay quanh để tạo tầng () từ vòng ()
floor(f) = round(f-0.5)
Mặc dù cả hai quy tắc này đều vi phạm các số âm, do đó, việc sử dụng nó là ít lý tưởng hơn:
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))
def trunc(f,n):
return ('%.16f' % f)[:(n-16)]
Một chức năng chung và đơn giản để sử dụng:
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
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
Theo tôi, hầu hết các câu trả lời đều quá phức tạp, còn điều này thì sao?
digits = 2 # Specify how many digits you want
fnum = '122.485221'
truncated_float = float(fnum[:fnum.find('.') + digits + 1])
>>> 122.48
Chỉ cần quét tìm chỉ mục của '.' và cắt ngắn như mong muốn (không làm tròn). Chuyển đổi chuỗi thành float ở bước cuối cùng.
Hoặc trong trường hợp của bạn nếu bạn nhận được một float làm đầu vào và muốn một chuỗi làm đầu ra:
fnum = str(122.485221) # convert float to string first
truncated_float = fnum[:fnum.find('.') + digits + 1] # string output
sử dụng numpy.round
import numpy as np
precision = 3
floats = [1.123123123, 2.321321321321]
new_float = np.round(floats, precision)
Một cái gì đó đủ đơn giản để phù hợp với danh sách dễ hiểu, không có thư viện hoặc các phụ thuộc bên ngoài khác. Đối với Python> = 3.6, rất đơn giản để viết với f-string.
Ý tưởng là để chuyển đổi chuỗi làm tròn đến một vị trí nhiều hơn bạn cần và sau đó cắt nhỏ chữ số cuối cùng.
>>> 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']
Tất nhiên, có được làm tròn xảy ra ở đây (cụ thể là cho chữ số thứ tư), nhưng làm tròn tại một số điểm là unvoidable. Trong trường hợp quá trình chuyển đổi giữa cắt ngắn và làm tròn có liên quan, đây là một ví dụ tốt hơn một chút:
>>> 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']
Phần thưởng: loại bỏ các số không ở bên phải
>>> 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']
Ý tưởng cốt lõi được đưa ra ở đây đối với tôi dường như là cách tiếp cận tốt nhất cho vấn đề này. Thật không may, nó đã nhận được ít phiếu bầu hơn trong khi câu trả lời sau đó có nhiều phiếu bầu hơn lại không hoàn chỉnh (như quan sát trong các nhận xét). Hy vọng rằng cách thực hiện dưới đây cung cấp một giải pháp ngắn gọn và đầy đủ cho việc cắt bớt .
def trunc(num, digits):
l = str(float(num)).split('.')
digits = min(len(l[1]), digits)
return (l[0]+'.'+l[1][:digits])
sẽ xử lý tất cả các trường hợp góc được tìm thấy ở đây và ở đây .
Tôi cũng là một người mới chơi trăn và sau khi sử dụng một số bit và mảnh ở đây, tôi cung cấp hai xu của mình
print str(int(time.time()))+str(datetime.now().microsecond)[:3]
str (int (time.time ())) sẽ lấy kỷ nguyên thời gian là int và chuyển đổi nó thành chuỗi và kết hợp với ... str (datetime.now (). microsecond) [: 3] chỉ trả về micro giây, chuyển đổi để xâu chuỗi và cắt bớt 3 ký tự đầu tiên
# value value to be truncated
# n number of values after decimal
value = 0.999782
n = 3
float(int(value*1en))*1e-n