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 Decimal
de um float
que pode fazer com que você acabe com algo diferente do número que você coloca lá. Eu acho que Decimal
funciona melhor quando a aritmética permanece Decimal
e 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
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.