Я хочу видалити цифри з поплавця, щоб мати фіксовану кількість цифр після крапки, наприклад:
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 .
Дуже рідко можна працювати зі значенням з плаваючою комою, яке наближається до круглого числа, але все ж навмисно не дорівнює цьому круглому числу. Тож під час обрізки, мабуть, є сенс вибирати «найприємніше» десяткове подання з усіх, що могли б відповідати значенню в пам'яті. 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
Якщо це ваша ситуація, якщо ви дійсно хочете використовувати «добрі» десяткові подання для усічення, все , що можна зробити (наскільки я знаю) , це вибрати кілька цифр, менше повна точність представима 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)
Дивіться документацію Python про типові типи . Вам потрібно буде трохи прокрутити вниз, щоб перейти до функції круглих. По суті, друге число говорить про те, скільки десяткових знаків для округлення.
Результат 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)
є 1.235
і ні1.2350000000000001
n = 1.923328437452
str(n)[:4]
2
, у вас буде десяткова крапка .
в кінці рядка - я думаю, не дуже вдале рішення.
Простий скрипт python -
n = 1.923328437452
n = float(int(n * 1000))
n /=1000
Справді пітонічний спосіб це зробити
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 усікаючий поплавок", концепція, яка справді проста, і яка заслуговує на кращі відповіді. Я погоджуюся з Хеткінсом, що використання decimal
модуля - це пітонічний спосіб зробити це, тому я надаю тут функцію, на яку, на мою думку, відповідає правильно на питання, і яка працює так, як очікувалося у всіх випадках.
Як бічна примітка, дробові значення, як правило, не можуть бути представлені точно бінарними змінними з плаваючою точкою (див. Тут для обговорення цього), тому моя функція повертає рядок.
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]
Під час використання pandas 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 ()" of
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))
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
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
Більшість відповідей занадто складні на мій погляд, як щодо цього?
digits = 2 # Specify how many digits you want
fnum = '122.485221'
truncated_float = float(fnum[:fnum.find('.') + digits + 1])
>>> 122.48
Просто скануйте на індекс "." і обрізати за бажанням (без округлення). Перетворити рядок у плаваючу як завершальний крок.
Або у вашому випадку, якщо ви отримуєте поплавок у якості введення і хочете рядок як вихід:
fnum = str(122.485221) # convert float to string first
truncated_float = fnum[:fnum.find('.') + digits + 1] # string output
використовувати numpy.round
import numpy as np
precision = 3
floats = [1.123123123, 2.321321321321]
new_float = np.round(floats, precision)
Щось досить просте, щоб вписатись у розуміння списку, без бібліотек та інших зовнішніх залежностей. Для Python> = 3.6, дуже просто писати за допомогою f-рядків.
Ідея полягає в тому, щоб перетворення рядків здійснило округлення до іншого місця, ніж вам потрібно, а потім відрізати останню цифру.
>>> 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']
Звичайно, тут відбувається округлення (а саме для четвертої цифри), але округлення в якийсь момент неминуче. Якщо перехід між усіченням і округленням є релевантним, ось дещо кращий приклад:
>>> 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 ())) сприймає епоху часу як 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