Tôi cần làm tròn một float để được hiển thị trong UI. Ví dụ, với một con số quan trọng:
1234 -> 1000
0,12 -> 0,1
0,012 -> 0,01
0,062 -> 0,06
6253 -> 6000
1999 -> 2000
Có một cách hay để làm điều này bằng thư viện Python hay tôi phải tự viết nó?
Tôi cần làm tròn một float để được hiển thị trong UI. Ví dụ, với một con số quan trọng:
1234 -> 1000
0,12 -> 0,1
0,012 -> 0,01
0,062 -> 0,06
6253 -> 6000
1999 -> 2000
Có một cách hay để làm điều này bằng thư viện Python hay tôi phải tự viết nó?
Câu trả lời:
Bạn có thể sử dụng số âm để làm tròn số nguyên:
>>> round(1234, -3)
1000.0
Do đó, nếu bạn chỉ cần chữ số có nghĩa nhất:
>>> from math import log10, floor
>>> def round_to_1(x):
... return round(x, -int(floor(log10(abs(x)))))
...
>>> round_to_1(0.0232)
0.02
>>> round_to_1(1234243)
1000000.0
>>> round_to_1(13)
10.0
>>> round_to_1(4)
4.0
>>> round_to_1(19)
20.0
Có lẽ bạn sẽ phải chăm sóc biến float thành số nguyên nếu nó lớn hơn 1.
log10là cách thích hợp duy nhất để xác định làm thế nào để làm tròn nó.
log10(abs(x)), nếu không các số âm sẽ thất bại ( x == 0Tất nhiên là xử lý riêng)
round_to_n = lambda x, n: x if x == 0 else round(x, -int(math.floor(math.log10(abs(x)))) + (n - 1))bảo vệ chống lại x==0và x<0cảm ơn bạn @RoyHyunjinHan và @TobiasKienzler. Không được bảo vệ chống lại không xác định như math.inf hoặc rác như Không, v.v.
% g trong định dạng chuỗi sẽ định dạng một số float được làm tròn đến một số số liệu có ý nghĩa. Đôi khi, nó sẽ sử dụng ký hiệu khoa học 'e', vì vậy hãy chuyển đổi chuỗi được làm tròn trở lại thành float sau đó thông qua định dạng chuỗi% s.
>>> '%s' % float('%.1g' % 1234)
'1000'
>>> '%s' % float('%.1g' % 0.12)
'0.1'
>>> '%s' % float('%.1g' % 0.012)
'0.01'
>>> '%s' % float('%.1g' % 0.062)
'0.06'
>>> '%s' % float('%.1g' % 6253)
'6000.0'
>>> '%s' % float('%.1g' % 1999)
'2000.0'
0.075đến 0.08. Nó trở lại 0.07thay thế.
round_sig = lambda f,p: float(('%.' + str(p) + 'e') % f)cho phép bạn điều chỉnh số lượng chữ số có nghĩa!
Nếu bạn muốn có hơn 1 số thập phân có ý nghĩa (nếu không giống như Evgeny):
>>> from math import log10, floor
>>> def round_sig(x, sig=2):
... return round(x, sig-int(floor(log10(abs(x))))-1)
...
>>> round_sig(0.0232)
0.023
>>> round_sig(0.0232, 1)
0.02
>>> round_sig(1234243, 3)
1230000.0
0.075đến 0.08. Nó trở lại 0.07thay thế.
round. docs.python.org/2/tutorial/floatingpoint.html#tut-fp-issues
f'{float(f"{i:.1g}"):g}'
# Or with Python <3.6,
'{:g}'.format(float('{:.1g}'.format(i)))
Giải pháp này khác với tất cả các giải pháp khác vì:
Đối với một số lượng nđáng kể các số liệu quan trọng, bạn có thể sử dụng:
print('{:g}'.format(float('{:.{p}g}'.format(i, p=n))))
Kiểm tra:
a = [1234, 0.12, 0.012, 0.062, 6253, 1999, -3.14, 0., -48.01, 0.75]
b = ['{:g}'.format(float('{:.1g}'.format(i))) for i in a]
# b == ['1000', '0.1', '0.01', '0.06', '6000', '2000', '-3', '0', '-50', '0.8']
Lưu ý : với giải pháp này, không thể tự động điều chỉnh số lượng các số liệu có ý nghĩa từ đầu vào vì không có cách tiêu chuẩn để phân biệt các số với các số 0 khác nhau ( 3.14 == 3.1400). Nếu bạn cần làm như vậy, thì các hàm không chuẩn như các hàm được cung cấp trong gói chính xác là cần thiết.
:gđịnh dạng bảo toàn số nguyên.
2000.0 gợi ý 5 chữ số có nghĩa, do đó nó phải đi qua {:g}lại.) Nói chung, các số nguyên có các số 0 ở cuối không rõ ràng đối với các số liệu có ý nghĩa, trừ khi một số kỹ thuật (như vượt quá mức đáng kể cuối cùng) được sử dụng.
Tôi đã tạo ra gói chính xác để làm những gì bạn muốn. Nó cho phép bạn đưa ra con số của mình nhiều hơn hoặc ít hơn.
Nó cũng đưa ra các ký hiệu tiêu chuẩn, khoa học và kỹ thuật với một số lượng đáng kể các số liệu quan trọng.
Trong câu trả lời được chấp nhận có dòng
>>> round_to_1(1234243)
1000000.0
Điều đó thực sự chỉ định 8 quả sung sig. Đối với số 1234243, thư viện của tôi chỉ hiển thị một con số đáng kể:
>>> from to_precision import to_precision
>>> to_precision(1234243, 1, 'std')
'1000000'
>>> to_precision(1234243, 1, 'sci')
'1e6'
>>> to_precision(1234243, 1, 'eng')
'1e6'
Nó cũng sẽ làm tròn con số đáng kể cuối cùng và có thể tự động chọn ký hiệu nào sẽ sử dụng nếu ký hiệu không được chỉ định:
>>> to_precision(599, 2)
'600'
>>> to_precision(1164, 2)
'1.2e3'
lambda x: to_precision(x, 2)
Để làm tròn một số nguyên thành 1 con số đáng kể, ý tưởng cơ bản là chuyển đổi nó thành một dấu phẩy động có 1 chữ số trước điểm và làm tròn số đó, sau đó chuyển đổi nó trở lại kích thước số nguyên ban đầu.
Để làm điều này, chúng ta cần biết công suất lớn nhất nhỏ hơn 10 so với số nguyên. Chúng ta có thể sử dụng sàn của hàm log 10 cho việc này.
from math import log10, floor def round_int(i,places): if i == 0: return 0 isign = i/abs(i) i = abs(i) if i < 1: return 0 max10exp = floor(log10(i)) if max10exp+1 < places: return i sig10pow = 10**(max10exp-places+1) floated = i*1.0/sig10pow defloated = round(floated)*sig10pow return int(defloated*isign)
Để trả lời trực tiếp câu hỏi, đây là phiên bản của tôi bằng cách đặt tên từ hàm R :
import math
def signif(x, digits=6):
if x == 0 or not math.isfinite(x):
return x
digits -= math.ceil(math.log10(abs(x)))
return round(x, digits)
Lý do chính của tôi để đăng câu trả lời này là các ý kiến phàn nàn rằng "0,075" làm tròn thành 0,07 thay vì 0,08. Điều này là do, được chỉ ra bởi "Novice C", với sự kết hợp của số học dấu phẩy động có cả độ chính xác hữu hạn và biểu diễn cơ số 2 . Số gần nhất với 0,075 thực sự có thể được biểu diễn nhỏ hơn một chút, do đó làm tròn xuất hiện khác với những gì bạn có thể mong đợi một cách ngây thơ.
Cũng lưu ý rằng điều này áp dụng cho bất kỳ việc sử dụng số học dấu phẩy động không thập phân nào, ví dụ cả C và Java đều có cùng một vấn đề.
Để hiển thị chi tiết hơn, chúng tôi yêu cầu Python định dạng số theo định dạng "hex":
0.075.hex()
cung cấp cho chúng tôi : 0x1.3333333333333p-4. Lý do để làm điều này là biểu diễn thập phân bình thường thường liên quan đến làm tròn và do đó không phải là cách máy tính thực sự "nhìn thấy" số. Nếu bạn chưa quen với định dạng này, một vài tài liệu tham khảo hữu ích là tài liệu Python và tiêu chuẩn C .
Để cho thấy những con số này hoạt động như thế nào một chút, chúng ta có thể quay lại điểm xuất phát của mình bằng cách thực hiện:
0x13333333333333 / 16**13 * 2**-4
Nên in ra 0.075. 16**13là bởi vì có 13 chữ số thập lục phân sau dấu thập phân và 2**-4là bởi vì số mũ hex là cơ số 2.
Bây giờ chúng tôi có một số ý tưởng về cách nổi được thể hiện, chúng tôi có thể sử dụng decimalmô-đun để cung cấp cho chúng tôi chính xác hơn, cho chúng tôi thấy những gì đang xảy ra:
from decimal import Decimal
Decimal(0x13333333333333) / 16**13 / 2**4
đưa ra: 0.07499999999999999722444243844và hy vọng giải thích lý do tại sao round(0.075, 2)đánh giá0.07
0.074999999999999999, bạn sẽ mong đợi gì trong trường hợp đó?
def round_to_n(x, n):
if not x: return 0
power = -int(math.floor(math.log10(abs(x)))) + (n - 1)
factor = (10 ** power)
return round(x * factor) / factor
round_to_n(0.075, 1) # 0.08
round_to_n(0, 1) # 0
round_to_n(-1e15 - 1, 16) # 1000000000000001.0
Hy vọng sử dụng tốt nhất tất cả các câu trả lời ở trên (trừ việc có thể đặt nó dưới dạng lambda một dòng;)). Chưa khám phá, hãy chỉnh sửa câu trả lời này:
round_to_n(1e15 + 1, 11) # 999999999999999.9
Tôi đã sửa đổi giải pháp của indgar để xử lý số âm và số nhỏ (bao gồm số không).
from math import log10, floor
def round_sig(x, sig=6, small_value=1.0e-9):
return round(x, sig - int(floor(log10(max(abs(x), abs(small_value))))) - 1)
x == 0? Nếu bạn yêu thích một lớp lót, chỉ cần return 0 if x==0 else round(...).
0.970 == 0.97). Tôi nghĩ rằng bạn có thể sử dụng một số giải pháp in khác như f'{round_sig(0.9701, sig=3):0.3f}'nếu bạn muốn in số không.
Nếu bạn muốn làm tròn mà không liên quan đến chuỗi, liên kết tôi tìm thấy bị chôn vùi trong các ý kiến trên:
http://code.activestate.com/lists/python-tutor/70739/
tấn công tôi là tốt nhất. Sau đó, khi bạn in với bất kỳ mô tả định dạng chuỗi nào, bạn sẽ có được đầu ra hợp lý và bạn có thể sử dụng biểu diễn số cho các mục đích tính toán khác.
Mã tại liên kết là một lớp lót ba: def, doc và return. Nó có một lỗi: bạn cần kiểm tra sự bùng nổ logarit. Điều đó thật dễ. So sánh đầu vào với sys.float_info.min. Giải pháp hoàn chỉnh là:
import sys,math
def tidy(x, n):
"""Return 'x' rounded to 'n' significant digits."""
y=abs(x)
if y <= sys.float_info.min: return 0.0
return round( x, int( n-math.ceil(math.log10(y)) ) )
Nó hoạt động cho bất kỳ giá trị số vô hướng nào và n có thể là một floatnếu bạn cần thay đổi phản hồi vì một số lý do. Bạn thực sự có thể đẩy giới hạn tới:
sys.float_info.min*sys.float_info.epsilon
mà không gây ra lỗi, nếu vì một lý do nào đó bạn đang làm việc với các giá trị rất nhỏ.
Tôi không thể nghĩ ra bất cứ điều gì có thể xử lý việc này ra khỏi hộp. Nhưng nó được xử lý khá tốt cho các số dấu phẩy động.
>>> round(1.2322, 2)
1.23
Số nguyên là khó khăn hơn. Chúng không được lưu trữ dưới dạng cơ sở 10 trong bộ nhớ, vì vậy những nơi quan trọng không phải là điều tự nhiên phải làm. Nó khá tầm thường để thực hiện một khi chúng là một chuỗi.
Hoặc cho số nguyên:
>>> def intround(n, sigfigs):
... n = str(n)
... return n[:sigfigs] + ('0' * (len(n)-(sigfigs)))
>>> intround(1234, 1)
'1000'
>>> intround(1234, 2)
Nếu bạn muốn tạo một hàm xử lý bất kỳ số nào, sở thích của tôi sẽ là chuyển đổi cả hai thành chuỗi và tìm vị trí thập phân để quyết định nên làm gì:
>>> def roundall1(n, sigfigs):
... n = str(n)
... try:
... sigfigs = n.index('.')
... except ValueError:
... pass
... return intround(n, sigfigs)
Một lựa chọn khác là kiểm tra loại. Điều này sẽ kém linh hoạt hơn rất nhiều và có thể sẽ không chơi độc đáo với các số khác như Decimalcác đối tượng:
>>> def roundall2(n, sigfigs):
... if type(n) is int: return intround(n, sigfigs)
... else: return round(n, sigfigs)
Câu trả lời được đăng là tốt nhất có sẵn khi được đưa ra, nhưng nó có một số hạn chế và không tạo ra các số liệu quan trọng chính xác về mặt kỹ thuật.
numpy.format_float_pose điều kiện hỗ trợ trực tiếp hành vi mong muốn. Đoạn sau trả về số float xđược định dạng thành 4 hình quan trọng, với ký hiệu khoa học bị loại bỏ.
import numpy as np
x=12345.6
np.format_float_positional(x, precision=4, unique=False, fractional=False, trim='k')
> 12340.
print(*[''.join([np.format_float_positional(.01*a*n,precision=2,unique=False,fractional=False,trim='k',pad_right=5) for a in [.99, .999, 1.001]]) for n in [8,9,10,11,12,19,20,21]],sep='\n'). Tôi đã không kiểm tra Dragon4.
Tôi cũng gặp phải vấn đề này nhưng tôi cần kiểm soát kiểu làm tròn. Vì vậy, tôi đã viết một hàm nhanh (xem mã bên dưới) có thể lấy giá trị, kiểu làm tròn và các chữ số có nghĩa mong muốn vào tài khoản.
import decimal
from math import log10, floor
def myrounding(value , roundstyle='ROUND_HALF_UP',sig = 3):
roundstyles = [ 'ROUND_05UP','ROUND_DOWN','ROUND_HALF_DOWN','ROUND_HALF_UP','ROUND_CEILING','ROUND_FLOOR','ROUND_HALF_EVEN','ROUND_UP']
power = -1 * floor(log10(abs(value)))
value = '{0:f}'.format(value) #format value to string to prevent float conversion issues
divided = Decimal(value) * (Decimal('10.0')**power)
roundto = Decimal('10.0')**(-sig+1)
if roundstyle not in roundstyles:
print('roundstyle must be in list:', roundstyles) ## Could thrown an exception here if you want.
return_val = decimal.Decimal(divided).quantize(roundto,rounding=roundstyle)*(decimal.Decimal(10.0)**-power)
nozero = ('{0:f}'.format(return_val)).rstrip('0').rstrip('.') # strips out trailing 0 and .
return decimal.Decimal(nozero)
for x in list(map(float, '-1.234 1.2345 0.03 -90.25 90.34543 9123.3 111'.split())):
print (x, 'rounded UP: ',myrounding(x,'ROUND_UP',3))
print (x, 'rounded normal: ',myrounding(x,sig=3))
Sử dụng định dạng kiểu python 2.6+ mới (vì% -style không được dùng nữa):
>>> "{0}".format(float("{0:.1g}".format(1216)))
'1000.0'
>>> "{0}".format(float("{0:.1g}".format(0.00356)))
'0.004'
Trong python 2.7+, bạn có thể bỏ qua các 0s hàng đầu .
Hàm này thực hiện một vòng bình thường nếu số lớn hơn 10 ** (- binary_poseitions), nếu không thì thêm số thập phân cho đến khi đạt được số lượng vị trí thập phân có ý nghĩa:
def smart_round(x, decimal_positions):
dp = - int(math.log10(abs(x))) if x != 0.0 else int(0)
return round(float(x), decimal_positions + dp if dp > 0 else decimal_positions)
Hy vọng nó giúp.
https://stackoverflow.com/users/1391441/gabriel , địa chỉ nào sau đây bạn quan tâm về rnd (.075, 1)? Caveat: trả về giá trị dưới dạng float
def round_to_n(x, n):
fmt = '{:1.' + str(n) + 'e}' # gives 1.n figures
p = fmt.format(x).split('e') # get mantissa and exponent
# round "extra" figure off mantissa
p[0] = str(round(float(p[0]) * 10**(n-1)) / 10**(n-1))
return float(p[0] + 'e' + p[1]) # convert str to float
>>> round_to_n(750, 2)
750.0
>>> round_to_n(750, 1)
800.0
>>> round_to_n(.0750, 2)
0.075
>>> round_to_n(.0750, 1)
0.08
>>> math.pi
3.141592653589793
>>> round_to_n(math.pi, 7)
3.141593
Điều này trả về một chuỗi, do đó, kết quả không có các phần phân số và các giá trị nhỏ xuất hiện trong ký hiệu E sẽ được hiển thị chính xác:
def sigfig(x, num_sigfig):
num_decplace = num_sigfig - int(math.floor(math.log10(abs(x)))) - 1
return '%.*f' % (num_decplace, round(x, num_decplace))
Đưa ra một câu hỏi được trả lời kỹ lưỡng tại sao không thêm một câu hỏi khác
Điều này phù hợp với thẩm mỹ của tôi tốt hơn một chút, mặc dù nhiều điều ở trên là tương đương
import numpy as np
number=-456.789
significantFigures=4
roundingFactor=significantFigures - int(np.floor(np.log10(np.abs(number)))) - 1
rounded=np.round(number, roundingFactor)
string=rounded.astype(str)
print(string)
Điều này hoạt động cho các số riêng lẻ và mảng numpy, và sẽ hoạt động tốt cho các số âm.
Có một bước bổ sung mà chúng tôi có thể thêm - np.round () trả về một số thập phân ngay cả khi được làm tròn là một số nguyên (ví dụ: có nghĩa là đáng kể = 2, chúng tôi có thể mong đợi lấy lại -460 nhưng thay vào đó chúng tôi nhận được -460.0). Chúng ta có thể thêm bước này để sửa lỗi cho điều đó:
if roundingFactor<=0:
rounded=rounded.astype(int)
Thật không may, bước cuối cùng này sẽ không hoạt động đối với một dãy số - Tôi sẽ để lại cho bạn đọc thân yêu để tìm ra nếu bạn cần.
import math
def sig_dig(x, n_sig_dig):
num_of_digits = len(str(x).replace(".", ""))
if n_sig_dig >= num_of_digits:
return x
n = math.floor(math.log10(x) + 1 - n_sig_dig)
result = round(10 ** -n * x) * 10 ** n
return float(str(result)[: n_sig_dig + 1])
>>> sig_dig(1234243, 3)
>>> sig_dig(243.3576, 5)
1230.0
243.36