Quero remover dígitos de um ponto flutuante para ter um número fixo de dígitos após o ponto, como:
1.923328437452 -> 1.923
Preciso dar saída como string para outra função, não imprimir.
Também quero ignorar os dígitos perdidos, não arredondá-los.
Quero remover dígitos de um ponto flutuante para ter um número fixo de dígitos após o ponto, como:
1.923328437452 -> 1.923
Preciso dar saída como string para outra função, não imprimir.
Também quero ignorar os dígitos perdidos, não arredondá-los.
Respostas:
Primeiro, a função, para aqueles que querem apenas um código de copiar e colar:
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]])
Isso é válido no Python 2.7 e 3.1+. Para versões mais antigas, não é possível obter o mesmo efeito de "arredondamento inteligente" (pelo menos, não sem muitos códigos complicados), mas arredondar para 12 casas decimais antes do truncamento funcionará na maior parte do tempo:
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]])
O núcleo do método subjacente é converter o valor em uma string com precisão total e, em seguida, cortar tudo além do número desejado de caracteres. A última etapa é fácil; pode ser feito com manipulação de string
i, p, d = s.partition('.')
'.'.join([i, (d+'0'*n)[:n]])
ou o decimalmódulo
str(Decimal(s).quantize(Decimal((0, (1,), -n)), rounding=ROUND_DOWN))
A primeira etapa, a conversão para uma string, é bastante difícil porque existem alguns pares de literais de ponto flutuante (ou seja, o que você escreve no código-fonte) que produzem a mesma representação binária e, ainda assim, devem ser truncados de forma diferente. Por exemplo, considere 0,3 e 0,29999999999999998. Se você escrever 0.3em um programa Python, o compilador o codifica usando o formato de ponto flutuante IEEE na sequência de bits (assumindo um float de 64 bits)
0011111111010011001100110011001100110011001100110011001100110011
Este é o valor mais próximo de 0,3 que pode ser representado com precisão como um flutuador IEEE. Mas se você escrever 0.29999999999999998em um programa Python, o compilador o traduzirá exatamente no mesmo valor . Em um caso, você pretendia que fosse truncado (para um dígito) como 0.3, enquanto no outro caso você pretendia que fosse truncado como 0.2, mas Python pode dar apenas uma resposta. Esta é uma limitação fundamental do Python, ou mesmo de qualquer linguagem de programação sem avaliação preguiçosa. A função de truncamento só tem acesso ao valor binário armazenado na memória do computador, não à string que você realmente digitou no código-fonte. 1
Se você decodificar a sequência de bits de volta para um número decimal, novamente usando o formato de ponto flutuante IEEE de 64 bits, você obtém
0.2999999999999999888977697537484345957637...
portanto, uma implementação ingênua surgiria, 0.2mesmo que provavelmente não seja o que você deseja. Para obter mais informações sobre o erro de representação de ponto flutuante, consulte o tutorial Python .
É muito raro trabalhar com um valor de ponto flutuante tão próximo a um número redondo e, ainda assim, intencionalmente não igual a esse número redondo. Portanto, ao truncar, provavelmente faz sentido escolher a representação decimal "mais adequada" de todas as que podem corresponder ao valor na memória. O Python 2.7 e superior (mas não 3.0) inclui um algoritmo sofisticado para fazer exatamente isso , que podemos acessar por meio da operação de formatação de string padrão.
'{}'.format(f)
A única ressalva é que isso funciona como uma gespecificação de formato, no sentido de que usa notação exponencial ( 1.23e+4) se o número for grande ou pequeno o suficiente. Portanto, o método deve capturar esse caso e tratá-lo de maneira diferente. Existem alguns casos em que o uso de uma fespecificação de formato causa um problema, como tentar truncar 3e-10para 28 dígitos de precisão (produz 0.0000000002999999999999999980), e ainda não tenho certeza da melhor forma de lidar com isso.
Se você realmente estiver trabalhando com floats que são muito próximos de números redondos, mas intencionalmente não iguais a eles (como 0,29999999999999998 ou 99,959999999999994), isso produzirá alguns falsos positivos, ou seja, arredondará números que você não deseja arredondar. Nesse caso, a solução é especificar uma precisão fixa.
'{0:.{1}f}'.format(f, sys.float_info.dig + n + 2)
O número de dígitos de precisão a ser usado aqui não importa realmente, ele só precisa ser grande o suficiente para garantir que qualquer arredondamento executado na conversão de string não "aumente" o valor para sua bela representação decimal. Acho que sys.float_info.dig + n + 2pode ser suficiente em todos os casos, mas, se não, 2talvez seja necessário aumentar, e não faz mal fazê-lo.
Em versões anteriores do Python (até 2.6 ou 3.0), a formatação do número de ponto flutuante era muito mais rudimentar e costumava produzir coisas como
>>> 1.1
1.1000000000000001
Se esta é sua situação, se você não quiser usar "nice" representações decimais para truncagem, tudo o que você pode fazer (tanto quanto eu sei) é escolher um número de dígitos, menos do que o representável precisão total por um float, e volta a número com essa quantidade de dígitos antes de truncá-lo. Uma escolha típica é 12,
'%.12f' % f
mas você pode ajustar isso para se adequar aos números que está usando.
1 Bem ... eu menti. Tecnicamente, você pode instruir o Python a analisar novamente seu próprio código-fonte e extrair a parte correspondente ao primeiro argumento que você passa para a função de truncamento. Se esse argumento for um literal de ponto flutuante, você pode simplesmente cortá-lo com um certo número de casas após o ponto decimal e retornar isso. No entanto, essa estratégia não funciona se o argumento for uma variável, o que o torna bastante inútil. O seguinte é apresentado apenas para valor de entretenimento:
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
Generalizar isso para lidar com o caso em que você passa uma variável parece uma causa perdida, já que você teria que rastrear a execução do programa até encontrar o literal de ponto flutuante que deu à variável seu valor. Se houver um. A maioria das variáveis será inicializada a partir da entrada do usuário ou de expressões matemáticas, caso em que a representação binária é tudo o que existe.
applymap()). Talvez haja uma maneira de tornar toda a operação mais eficiente, mas isso seria um assunto para outra questão.
round(1.923328437452, 3)
Consulte a documentação do Python sobre os tipos padrão . Você precisará rolar um pouco para baixo para chegar à função round. Essencialmente, o segundo número diz para quantas casas decimais ele deve ser arredondado.
O resultado de roundé um float, então fique atento (o exemplo é do Python 2.6):
>>> round(1.923328437452, 3)
1.923
>>> round(1.23456, 3)
1.2350000000000001
Você ficará melhor ao usar uma string formatada:
>>> "%.3f" % 1.923328437452
'1.923'
>>> "%.3f" % 1.23456
'1.235'
round(1.23456, 3)é 1.235e não é1.2350000000000001
n = 1.923328437452
str(n)[:4]
2, você terá um ponto decimal .no final da string - não é realmente uma boa solução, eu acho.
Script python simples -
n = 1.923328437452
n = float(int(n * 1000))
n /=1000
A maneira verdadeiramente pythônica de fazer isso é
from decimal import *
with localcontext() as ctx:
ctx.rounding = ROUND_DOWN
print Decimal('1.923328437452').quantize(Decimal('0.001'))
ou mais curto:
from decimal import Decimal as D, ROUND_DOWN
D('1.923328437452').quantize(D('0.001'), rounding=ROUND_DOWN)
Atualizar
Normalmente, o problema não está em truncar os próprios flutuadores, mas no uso impróprio de números flutuantes antes do arredondamento.
Por exemplo: int(0.7*3*100)/100 == 2.09.
Se você for forçado a usar floats (digamos, você está acelerando seu código com numba), é melhor usar centavos como "representação interna" de preços: ( 70*3 == 210) e multiplicar / dividir as entradas / saídas.
int(0.7*3*100)/100 == 2.09. Para onde foi meu 1 centavo?
ImportError: cannot import name 'D', acredito que você queria fazer uma importação nomeada, não?
Muitas das respostas dadas para esta pergunta estão completamente erradas. Eles arredondam os flutuadores (em vez de truncar) ou não funcionam para todos os casos.
Este é o principal resultado do Google quando procuro por 'Python truncate float', um conceito realmente direto e que merece respostas melhores. Eu concordo com Hatchkins que usar o decimalmódulo é a maneira pítônica de fazer isso, então eu dou aqui uma função que acho que responde a pergunta corretamente e que funciona conforme o esperado para todos os casos.
Como uma nota lateral, valores fracionários, em geral, não podem ser representados exatamente por variáveis binárias de ponto flutuante (veja aqui uma discussão sobre isso), e é por isso que minha função retorna uma string.
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()
Eu fiz algo assim:
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
Você pode fazer:
def truncate(f, n):
return math.floor(f * 10 ** n) / 10 ** n
testando:
>>> f=1.923328437452
>>> [truncate(f, n) for n in range(5)]
[1.0, 1.9, 1.92, 1.923, 1.9233]
Ao usar um pandas df, isso funcionou para mim
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)
Só queria mencionar que o velho truque de "arredondar () com chão ()"
round(f) = floor(f+0.5)
pode ser girado para tornar o piso () de redondo ()
floor(f) = round(f-0.5)
Embora ambas as regras quebrem em torno de números negativos, usá-las é menos do que ideal:
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)]
Uma função geral e simples de usar:
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
A maioria das respostas é muito complicada na minha opinião, que tal isso?
digits = 2 # Specify how many digits you want
fnum = '122.485221'
truncated_float = float(fnum[:fnum.find('.') + digits + 1])
>>> 122.48
Simplesmente digitalizando o índice de '.' e truncar conforme desejado (sem arredondamento). Converta a string em flutuante como etapa final.
Ou, no seu caso, se você receber um float como entrada e quiser uma string como saída:
fnum = str(122.485221) # convert float to string first
truncated_float = fnum[:fnum.find('.') + digits + 1] # string output
use numpy.round
import numpy as np
precision = 3
floats = [1.123123123, 2.321321321321]
new_float = np.round(floats, precision)
Algo simples o suficiente para caber em uma compreensão de lista, sem bibliotecas ou outras dependências externas. Para Python> = 3.6, é muito simples escrever com strings f.
A ideia é permitir que a conversão de string faça o arredondamento para um lugar a mais do que você precisa e, em seguida, corte o último dígito.
>>> 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']
Claro, há é arredondamento acontecendo aqui (nomeadamente para o quarto dígito), mas o arredondamento em algum momento é unvoidable. Caso a transição entre truncamento e arredondamento seja relevante, aqui está um exemplo um pouco melhor:
>>> 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']
Bônus: remover zeros à direita
>>> 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']
A ideia central apresentada aqui me parece ser a melhor abordagem para esse problema. Infelizmente, ele recebeu menos votos, enquanto a resposta posterior que tem mais votos não está completa (conforme observado nos comentários). Felizmente, a implementação a seguir fornece uma solução curta e completa para truncamento .
def trunc(num, digits):
l = str(float(num)).split('.')
digits = min(len(l[1]), digits)
return (l[0]+'.'+l[1][:digits])
que deve cuidar de todos os casos extremos encontrados aqui e aqui .
Também sou um novato em python e depois de usar alguns pedaços aqui, ofereço meus dois centavos
print str(int(time.time()))+str(datetime.now().microsecond)[:3]
str (int (time.time ())) pegará o período de tempo como int e o converterá em string e se unirá com ... str (datetime.now (). microssegundo) [: 3] que retorna apenas os microssegundos, converter para string e truncar para os primeiros 3 caracteres
# value value to be truncated
# n number of values after decimal
value = 0.999782
n = 3
float(int(value*1en))*1e-n