2009-04-23 2 views
75

Я хочу, чтобы удалить цифры с плавающей точкой, чтобы иметь фиксированное количество цифр после запятой, например:усечение поплавков в Python

1.923328437452 -> 1.923 

Мне нужно вывести в виде строки в другую функцию, а не печатать.

Также я хочу проигнорировать потерянные цифры, а не вокруг них.

+3

Если -1.233 быть укорочены до -1.23 или -1.24? –

+0

@Mad Physicist Вы имеете в виду, что округление -1.233 может оказаться равным -1.24? Я имею в виду усечение до минус бесконечности. –

+0

OIC. Это было глупо. Удаление комментария. –

ответ

81

Во-первых, функция для тех, кто просто хочет сома e код копирования и вставки:

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 или даже любой язык программирования без ленивой оценки.Функция усечения имеет доступ только к двоичному значению, хранящемуся в памяти компьютера, а не к строке, которую вы действительно ввели в исходный код.

Если вы декодировать последовательность битов обратно в десятичное число, снова используя 64-битный формат IEEE с плавающей точкой, вы получите

0.2999999999999999888977697537484345957637... 

так наивная реализация могла бы придумать 0.2 хотя это, вероятно, не то, что вы хотите. Подробнее о ошибке с плавающей точкой, see the Python tutorial.

Очень редко работать со значением с плавающей точкой, близким к круглому числу, и все же является намеренно, не равным этому круглому числу. Поэтому при усечении, вероятно, имеет смысл выбрать «самое приятное» десятичное представление из всего, что может соответствовать значению в памяти. Python 2.7 и выше (но не 3.0) включает в себя sophisticated algorithm to do just that, с которым мы можем получить доступ через операцию форматирования строк по умолчанию.

'{}'.format(f) 

Единственное ограничение в том, что это действует как спецификации формата g, в том смысле, что он использует экспоненциальное обозначение (1.23e+4), если число является большим или достаточно мало. Поэтому метод должен поймать этот случай и обрабатывать его по-разному. Есть несколько случаев, когда использование спецификации формата f вызывает проблему, например, попытку обрезания 3e-10 до 28 цифр точности (она производит 0.0000000002999999999999999980), и я еще не знаю, как лучше всего их обрабатывать.

Если вы на самом деле являетесь работой с float с, которые очень близки к округлить, но намеренно не приравненные к ним (как 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 

Если это ваша ситуация, если вы do хотите использовать «приятные» десятичные представления для усечения, все, что вы можете сделать (насколько мне известно), - выбрать некоторое количество цифр, меньше полной точности, представляемой float, и округлить число до этого большого числа цифр перед усечением. Типичный выбор - 12,

'%.12f' % f 

, но вы можете отрегулировать его в соответствии с числами, которые вы используете.


Ну ... Я соврал.Технически вы можете поручить 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 

Обобщая это обрабатывает случай, когда вы передаете в переменном, кажется, как безнадежны, так как вы должны проследить назад через выполнение программы, пока вы не найдете: представлена ​​только для развлечения следующего с плавающей точкой, который дал переменной значение. Если есть даже один. Большинство переменных будут инициализированы из пользовательского ввода или математических выражений, и в этом случае двоичное представление есть все, что есть.

126
round(1.923328437452, 3) 

См. Python's documentation on the standard types. Вам нужно немного прокрутить список, чтобы перейти к круглой функции. По существу второе число указывает, сколько десятичных знаков округлить до.

+1

sweeet, я не знал, что существует функция round() - я могу использовать это! (Я бы поднял, но это не ответ на вопрос OP ...) –

+0

Ну, математика 1-го класса учит, что раунд (1.9265, 3) IS, в конце концов, 1.927 ... –

+32

Я имел в виду округление не Что мне нужно. Мне нужно усечь, что другое. –

-3

Если вы имеете в виду при печати, то должно работать:

print '%.3f' % number 
+0

Вы имеете в виду «% .3f» –

+0

yep, вот что я имею в виду – user44511

+2

Это округляет число, оно не усекает. –

23

Результат round является поплавок, так что следите:

>>> round(1.923328437452, 3) 
1.923 
>>> round(1.23456, 3) 
1.2350000000000001 

Вам будет лучше при использовании форматированная строка:

>>> "%.3f" % 1.923328437452 
'1.923' 
>>> "%.3f" % 1.23456 
'1.235' 
+6

На моем Python этот раунд: '% .3f'% 1.23456 == '1.235' –

+0

@David: Вы абсолютно правы. –

7
def trunc(num, digits): 
    sp = str(num).split('.') 
    return '.'.join([sp[0], sp[:digits]]) 

Это должно сработать. Он должен дать вам усечение, которое вы ищете.

1
def trunc(f,n): 
    return ('%.16f' % f)[:(n-16)] 
1

Просто хотелось бы отметить, что старый "делать круглые() с полом()" трюк

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)) 
16
n = 1.923328437452 
str(n)[:4] 
+2

Простые и pythonic. 4 - это размер всего числа, но не только цифры после точки. – GaTTaCa

+3

Итак, если пользователь вводит, например, '2', вы получите десятичную точку' .' в конце строки - не очень хорошее решение, я думаю. – Zelphir

1

int (16.5); это даст целое значение 16, то есть TRUNC, не будет в состоянии определить десятичные, но думаю, вы можете сделать это

import math; 

def trunc(invalue, digits): 
    return int(invalue*math.pow(10,digits))/math.pow(10,digits); 
-1

Am также питон новичка и после использования некоторых кусочков здесь, я предлагаю мои два цента

print str(int(time.time()))+str(datetime.now().microsecond)[:3] 

ул (интермедиат (time.time())) будет время, эпоха, как межд и преобразовать его в строку и присоединиться к ... ул (DateTime.Now () .microsecond) [: 3], который возвращает только микросекунды, конвертировать в строку и обрезать до первых трех символов

7

поистине вещий способ сделать это является

from decimal import * 

with localcontext() as ctx: 
    ctx.rounding = ROUND_DOWN 
    print Decimal('1.923328437452').quantize(Decimal('0.001')) 
4

я сделал что-то вроде этого:

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 
0

Вот простой способ:

def truncate(num, res=3): 
    return (floor(num*pow(10, res)+0.5))/pow(10, res) 

для Num = +1,923328437452, это выходы 1.923

7

Так много ответов, предоставленных для этого вопроса, просто совершенно неверны. Они либо округляют всплывающие окна (а не обрезают) или не работают для всех случаев.

Это главный результат Google, когда я ищу «Python truncate float», концепцию, которая действительно проста, и которая заслуживает лучших ответов.Я согласен с Хатчинсом в том, что использование модуля decimal - это питонический способ сделать это, поэтому я даю здесь функцию, которая, как я думаю, правильно отвечает на вопрос и которая работает так, как ожидалось, для всех случаев.

В качестве побочной заметки дробные значения, в общем, не могут быть представлены точно бинарными переменными с плавающей запятой (см. here для обсуждения этого вопроса), поэтому моя функция возвращает строку.

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() 
3

Вы можете сделать:

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] 
+0

Это только обрезается с положительными числами, отрицательные числа округляются (от нуля). –

1

использование numpy.round

import numpy as np 
precision = 3 
floats = [1.123123123, 2.321321321321] 
new_float = np.round(floats, precision) 
-1
# value value to be truncated 
# n number of values after decimal 

value = 0.999782 
n = 3 
float(int(value*1en))*1e-n 
5

Простой питон скрипт -

n = 1.923328437452 
n = float(n*1000) 
n /= 1000 
+2

Чистый ответ. Вы просто пропустите один шаг, чтобы вернуться обратно к float, прежде чем делить на 1000. В противном случае вы получите 1. –

1

общая и простая функция использования:

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 
+0

Отлично!Приведение к int усекает как положительные, так и отрицательные числа. –

8

В моем Python 2.7 строки:

>>> int(1.923328437452 * 1000)/1000.0 1.923

0

>>> пол ((1,23658945) * 10 ** 4)/10 ** 4
1.2365

# разделяй и умножить на 10 ** количество требуемых цифр

0

Если вам нравятся некоторые mathemagic, это работает для анолита номеров:

>>> v = 1.923328437452 
>>> v - v % 1e-3 
1.923 
Смежные вопросы