Formatar carros alegóricos sem zeros à direita

181

Como formatar um flutuador para que não contenha zeros à direita? Em outras palavras, quero que a sequência resultante seja a mais curta possível.

Por exemplo:

3 -> "3"
3. -> "3"
3.0 -> "3"
3.1 -> "3.1"
3.14 -> "3.14"
3.140 -> "3.14"
TarGz
fonte
Esse exemplo não faz nenhum sentido. 3.14 == 3.140- Eles são o mesmo número de ponto flutuante. Para esse assunto, 3.140000 é o mesmo número de ponto flutuante. O zero não existe em primeiro lugar.
S.Lott
33
@ S.Lott - Eu acho que a questão é IMPRIMIR o número da bóia sem os zeros à direita, não a equivalência real de dois números.
Pokstad
1
@ pokstad: Nesse caso, não há zero "supérfluo". %0.2fe %0.3fsão os dois formatos necessários para produzir os últimos números à esquerda. Use %0.2fpara produzir os dois últimos números à direita.
S.Lott
6
3.0 -> "3"ainda é um caso de uso válido. print( '{:,g}'.format( X )trabalhou para eu produzir 3onde X = 6 / 2e quando X = 5 / 2obtive uma saída 2.5conforme o esperado.
ShoeMaker 27/02
1
pergunta antiga, mas .. print("%s"%3.140)dá o que você quer. (Eu adicionei uma resposta para baixo abaixo ...)
drevicko

Respostas:

178

Eu, eu faria ('%f' % x).rstrip('0').rstrip('.')- garante a formatação de ponto fixo em vez de notação científica, etc. etc. Sim, não tão elegante e elegante quanto %g, mas funciona (e não sei como forçar %ga nunca usar notação científica; -)

Alex Martelli
fonte
6
O único problema com isso é '%.2f' % -0.0001deixá-lo com -0.00e, finalmente -0.
Kos
3
@ alexanderlukanin13 porque a precisão padrão é 6, consulte docs.python.org/2/library/string.html : 'f' Fixed point. Displays the number as a fixed-point number. The default precision is 6.você precisaria usar '% 0.7f' na solução acima.
derenio
3
@derenio Bom ponto :-) Só posso acrescentar que aumentar a precisão acima '%0.15f'é uma má ideia, porque coisas estranhas começam a acontecer.
alexanderlukanin13
1
Caso você esteja no meio de alguma outra string: print('In the middle {} and something else'.format('{:f}'.format(a).rstrip('0')))
tolerante a falhas
1
@ Alex Martelli, livre-se do duplo rstripcomando. Use simplesmente esta vez para retirar tanto o ponto ( .) e todos os zeros à direita depois em uma única operação:('%f' % x).rstrip('.0')
Gabriel Staples
143

Você pode usar %gpara conseguir isso:

'%g'%(3.140)

ou, para Python 2.6 ou melhor:

'{0:g}'.format(3.140)

Dos documentos paraformat : gcausas (entre outras coisas)

zeros à direita insignificantes [a serem] removidos do significando e o ponto decimal também é removido se não houver dígitos restantes após ele.

unutbu
fonte
28
Ah, quase! Às vezes, formata o float em notação científica ("2.342E + 09") - é possível desativá-lo, ou seja, sempre mostra todos os dígitos significativos?
TarGz 14/03/10
5
Por que usar '{0:...}'.format(value)quando você poderia usar format(value, '...')? Isso evita a necessidade de analisar o especificador de formato de uma sequência de modelos que, de outra forma, está vazia.
Martijn Pieters
2
@MartijnPieters: O custo minúsculo de analisar o especificador de formato é inundado por outro AFAICT; na prática, meus benchmarks locais em 3.6 (com escopo de função do microbenchmark para modelar com precisão o código real) format(v, '2.5f')demoraram cerca de 10% a mais que '{:2.5f}'.format(v). Mesmo se não, eu costumo usar o strformulário do método, porque quando preciso ajustá-lo, adicionar valores adicionais etc., há menos alterações. Obviamente, a partir de 3.6, temos f-strings para a maioria dos propósitos. :-)
ShadowRanger 25/08
5
No Python 3.6, isso pode ser reduzido para f"{var:g}"onde varestá uma variável float.
TheGreatCabbage
12

Que tal tentar a abordagem mais fácil e provavelmente mais eficaz? O método normalize () remove todos os zeros à direita.

from decimal import Decimal

print (Decimal('0.001000').normalize())
# Result: 0.001

Funciona em Python 2 e Python 3 .

-- Atualizada --

O único problema como @ BobStein-VisiBone apontou é que números como 10, 100, 1000 ... serão exibidos em representação exponencial. Isso pode ser facilmente corrigido usando a seguinte função:

from decimal import Decimal


def format_float(f):
    d = Decimal(str(f));
    return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
Ander
fonte
2
Exceto Decimal('10.0').normalize()se torna'1E+1'
Bob Stein
11

Depois de examinar as respostas para várias perguntas semelhantes, esta parece ser a melhor solução para mim:

def floatToString(inputValue):
    return ('%.15f' % inputValue).rstrip('0').rstrip('.')

Meu raciocínio:

%g não se livra da notação científica.

>>> '%g' % 0.000035
'3.5e-05'

15 casas decimais parecem evitar comportamentos estranhos e têm muita precisão para minhas necessidades.

>>> ('%.15f' % 1.35).rstrip('0').rstrip('.')
'1.35'
>>> ('%.16f' % 1.35).rstrip('0').rstrip('.')
'1.3500000000000001'

Eu poderia ter usado em format(inputValue, '.15f').vez de '%.15f' % inputValue, mas isso é um pouco mais lento (~ 30%).

Eu poderia ter usado Decimal(inputValue).normalize(), mas isso também tem alguns problemas. Por um lado, é MUITO mais lento (~ 11x). Também descobri que, embora tenha uma precisão bastante grande, ainda sofre perda de precisão ao usá-lo normalize().

>>> Decimal('0.21000000000000000000000000006').normalize()
Decimal('0.2100000000000000000000000001')
>>> Decimal('0.21000000000000000000000000006')
Decimal('0.21000000000000000000000000006')

Mais importante, eu ainda estaria convertendo para Decimalde um floatque pode fazer com que você acabe com algo diferente do número que você coloca lá. Eu acho que Decimalfunciona melhor quando a aritmética permanece Decimale Decimalé inicializada com uma string.

>>> Decimal(1.35)
Decimal('1.350000000000000088817841970012523233890533447265625')
>>> Decimal('1.35')
Decimal('1.35')

Tenho certeza de que a questão da precisão de Decimal.normalize()pode ser ajustada ao que é necessário usando configurações de contexto, mas considerando a velocidade já baixa e não precisando de precisão ridícula e o fato de que eu ainda estaria convertendo de um flutuador e perdendo a precisão de qualquer maneira, eu não acho que valeu a pena perseguir.

Não estou preocupado com o possível resultado "-0", já que -0.0 é um número de ponto flutuante válido e provavelmente seria uma ocorrência rara, mas como você mencionou que deseja manter o resultado da string o mais curto possível, você sempre poderia usar um condicional extra com muito pouco custo de velocidade extra.

def floatToString(inputValue):
    result = ('%.15f' % inputValue).rstrip('0').rstrip('.')
    return '0' if result == '-0' else result
PolyMesh
fonte
1
Infelizmente, só funciona com números com menos do que aproximadamente) cinco ou mais dígitos à esquerda da casa decimal. floatToString(12345.6)retorna '12345.600000000000364'por exemplo. Diminuir 15 in %.15fpara um número menor resolve-o neste exemplo, mas esse valor precisa ser diminuído cada vez mais à medida que o número aumenta. Pode ser calculado dinamicamente com base na base 10 do log do número, mas isso rapidamente se torna muito complicado.
JohnSpeeks
1
Uma maneira de resolver esse problema poderia ser a de limitar o comprimento do número inteiro (em vez de apenas os dígitos após o decimal):result = ('%15f' % val).rstrip('0').rstrip('.').lstrip(' ')
Timothy Smith
@JohnSpeeks Não tenho certeza se isso é evitável. É um efeito colateral de números flutuantes não serem capazes de representar a precisão se forem necessários mais dígitos no lado esquerdo. Pelo que sei, o número que sai como uma string é o mesmo número que entra como float, ou pelo menos a representação mais próxima dela. >>>12345.600000000000364 == 12345.6 True
PolyMesh 7/03/19
Eu escrevi outra solução .
niitsuma 10/06
8

Aqui está uma solução que funcionou para mim. É uma mistura da solução do PolyMesh e o uso da nova .format() sintaxe .

for num in 3, 3., 3.0, 3.1, 3.14, 3.140:
    print('{0:.2f}'.format(num).rstrip('0').rstrip('.'))

Saída :

3
3
3
3.1
3.14
3.14
Kaushal Modi
fonte
A única coisa errada com este é que você deve definir um número razoável de dígitos decimais. Quanto mais alto você o define, mais números precisos você pode representar, mas se você fizer muito isso, poderá prejudicar o desempenho.
22717 beruic
1
Adicionando ao comentário do beruic, isso não funciona para carros alegóricos de maior precisão (por exemplo 3.141), pois .2fé codificado.
TrebledJ
3

Você pode simplesmente usar format () para conseguir isso:

format(3.140, '.10g') onde 10 é a precisão que você deseja.

clel
fonte
2
>>> str(a if a % 1 else int(a))
Shameem
fonte
Você não quer dizer int(a) if a % 1 else a ?
beruic
Caro Beruic, sua resposta resulta em resposta negativa. a if a % 1 else int(a)está correto. Pergunta precisa de saída em string, então acabei de adicionarstr
Shameem
Ah, entendi agora. a % 1é verdade porque é diferente de zero. Eu implícita e erroneamente percebi isso a % 1 == 0.
30717 beruic
2

Embora seja provável que a formatação seja a forma mais Pythonic, aqui está uma solução alternativa usando a more_itertools.rstripferramenta.

import more_itertools as mit


def fmt(num, pred=None):
    iterable = str(num)
    predicate = pred if pred is not None else lambda x: x in {".", "0"}
    return "".join(mit.rstrip(iterable, predicate))

assert fmt(3) == "3"
assert fmt(3.) == "3"
assert fmt(3.0) == "3"
assert fmt(3.1) == "3.1"
assert fmt(3.14) == "3.14"
assert fmt(3.140) == "3.14"
assert fmt(3.14000) == "3.14"
assert fmt("3,0", pred=lambda x: x in set(",0")) == "3"

O número é convertido em uma sequência, sem caracteres finais que atendem a um predicado. A definição da função fmtnão é necessária, mas é usada aqui para testar asserções, todas aprovadas. Nota: funciona em entradas de sequência e aceita predicados opcionais.

Veja também detalhes nesta biblioteca de terceiros more_itertools,.

pylang
fonte
1

Se você pode viver com 3. e 3.0 aparecendo como "3.0", uma abordagem muito simples que retira zeros à direita das representações flutuantes:

print("%s"%3.140)

(obrigado @ellimilial por apontar as exceções)

drevicko
fonte
1
Mas print("%s"%3.0)faz.
precisa saber é o seguinte
1

Usar o pacote QuantiPhy é uma opção. Normalmente, o QuantiPhy é usado ao trabalhar com números com unidades e fatores de escala de SI, mas possui diversas opções de formatação de números.

    >>> from quantiphy import Quantity

    >>> cases = '3 3. 3.0 3.1 3.14 3.140 3.14000'.split()
    >>> for case in cases:
    ...    q = Quantity(case)
    ...    print(f'{case:>7} -> {q:p}')
          3 -> 3
         3. -> 3
        3.0 -> 3
        3.1 -> 3.1
       3.14 -> 3.14
      3.140 -> 3.14
    3.14000 -> 3.14

E não usará notação eletrônica nesta situação:

    >>> cases = '3.14e-9 3.14 3.14e9'.split()
    >>> for case in cases:
    ...    q = Quantity(case)
    ...    print(f'{case:>7} -> {q:,p}')
    3.14e-9 -> 0
       3.14 -> 3.14
     3.14e9 -> 3,140,000,000

Uma alternativa que você pode preferir é usar fatores de escala de SI, talvez com unidades.

    >>> cases = '3e-9 3.14e-9 3 3.14 3e9 3.14e9'.split()
    >>> for case in cases:
    ...    q = Quantity(case, 'm')
    ...    print(f'{case:>7} -> {q}')
       3e-9 -> 3 nm
    3.14e-9 -> 3.14 nm
          3 -> 3 m
       3.14 -> 3.14 m
        3e9 -> 3 Gm
     3.14e9 -> 3.14 Gm
Autumn McClellan
fonte
0

O OP gostaria de remover zeros supérfluos e tornar a string resultante o mais curta possível.

Acho que a formatação exponencial% g reduz a cadeia resultante para valores muito grandes e muito pequenos. O problema ocorre com valores que não precisam de notação exponencial, como 128.0, que não é muito grande nem muito pequena.

Aqui está uma maneira de formatar números como cadeias curtas que usam a notação exponencial% g somente quando Decimal.normalize cria cadeias muito longas. Esta pode não ser a solução mais rápida (pois usa Decimal.normalize)

def floatToString (inputValue, precision = 3):
    rc = str(Decimal(inputValue).normalize())
    if 'E' in rc or len(rc) > 5:
        rc = '{0:.{1}g}'.format(inputValue, precision)        
    return rc

inputs = [128.0, 32768.0, 65536, 65536 * 2, 31.5, 1.000, 10.0]

outputs = [floatToString(i) for i in inputs]

print(outputs)

# ['128', '32768', '65536', '1.31e+05', '31.5', '1', '10']
kinok
fonte
0

Para float você pode usar isso:

def format_float(num):
    return ('%i' if num == int(num) else '%s') % num

Teste-o:

>>> format_float(1.00000)
'1'
>>> format_float(1.1234567890000000000)
'1.123456789'

Para Decimal, consulte a solução aqui: https://stackoverflow.com/a/42668598/5917543

Artem Skoretskiy
fonte
0

"{:.5g}".format(x)

Eu uso isso para formatar carros alegóricos para rastrear zeros.

martin sun
fonte
3143.93 -> 3.1e + 03
j35t3r
0

Aqui está a resposta:

import numpy

num1 = 3.1400
num2 = 3.000
numpy.format_float_positional(num1, 3, trim='-')
numpy.format_float_positional(num2, 3, trim='-')

saída "3.14" e "3"

trim='-' remove os zeros à direita e os decimais.

comport9
fonte
-1

Use% g com largura grande o suficiente, por exemplo '% .99g'. Ele será impresso em notação de ponto fixo para qualquer número razoavelmente grande.

EDIT: não funciona

>>> '%.99g' % 0.0000001
'9.99999999999999954748111825886258685613938723690807819366455078125e-08'
alexanderlukanin13
fonte
1
.99é precisão, não largura; meio útil, mas você não pode definir a precisão real dessa maneira (exceto truncá-la você mesmo).
Kos
-1

Você pode usar max()assim:

print(max(int(x), x))

elegível
fonte
1
você deve considerar o caso em que xé negativo. if x < 0: print(min(x), x) else : print(max(x), x)
ThunderPhoenix
Um método útil quando eu quero fazer o json stringify. float 1.0 mude para int 1, para que ele execute exatamente o mesmo que em javascript.
Ping19 de
-3

Você pode conseguir isso da maneira mais pitônica possível:

python3:

"{:0.0f}".format(num)
Lan Vukušič
fonte
Você está certo. . A maneira mais fácil é usar o "{:} g" formato (num)
Pyglouthon
-4

Manuseando% f e você deve colocar

% .2f

, onde: .2f == .00 flutua.

Exemplo:

imprimir "Preço:% .2f"% preços [produto]

resultado:

Preço: 1.50

Alex M.
fonte
1
Isto é claramente afirmado, não o que a pergunta quer. Precisa fazer voto negativo.
episodeyang