Форматирование поплавков без конечных нулей


181

Как я могу отформатировать число с плавающей запятой, чтобы оно не содержало конечных нулей? Другими словами, я хочу, чтобы полученная строка была максимально короткой.

Например:

3 -> "3"
3. -> "3"
3.0 -> "3"
3.1 -> "3.1"
3.14 -> "3.14"
3.140 -> "3.14"

Этот пример не имеет никакого смысла вообще. 3.14 == 3.140- Это одно и то же число с плавающей запятой. В этом отношении 3.140000 - это то же самое число с плавающей точкой. Ноль не существует в первую очередь.
S.Lott

33
@ S.Lott - я думаю, что проблема в ПЕЧАТИ номера с плавающей запятой без конечных нулей, а не в реальной эквивалентности двух чисел.
pokstad

1
@pokstad: в этом случае нет «лишнего» нуля. %0.2fи %0.3fдва формата, необходимые для получения последних чисел слева. Используйте %0.2fдля получения последних двух чисел справа.
S.Lott

6
3.0 -> "3"все еще действительный вариант использования. print( '{:,g}'.format( X )работал для меня, чтобы вывести, 3где X = 6 / 2и когда X = 5 / 2я получил вывод, 2.5как ожидалось.
ShoeMaker

1
старый вопрос, но .. print("%s"%3.140)дает вам то, что вы хотите. (Я добавил ответ внизу внизу ...)
drevicko

Ответы:


178

Я, я бы сделал ('%f' % x).rstrip('0').rstrip('.')- гарантирует форматирование с фиксированной запятой, а не научную нотацию и т. Д. Да, не так гладко и элегантно, как %g, но это работает (и я не знаю, как заставить %gникогда не использовать научную нотацию; -).


6
Единственная проблема с этим, это '%.2f' % -0.0001оставит вас с -0.00и в конечном итоге -0.
Кос

3
@ alexanderlukanin13, поскольку точность по умолчанию равна 6, см. docs.python.org/2/library/string.html : 'f' Fixed point. Displays the number as a fixed-point number. The default precision is 6.вам придется использовать «% 0.7f» в приведенном выше решении.
derenio

3
@derenio Хороший вопрос :-) Я могу только добавить, что повышение точности выше '%0.15f'- плохая идея, потому что странные вещи начинают происходить.
alexanderlukanin13

1
Если вы находитесь в середине какой-либо другой строки: print('In the middle {} and something else'.format('{:f}'.format(a).rstrip('0')))
отказоустойчивый

1
@ Алекс Мартелли, избавься от двойной rstripкоманды. Просто используйте это вместо этого, чтобы убрать точку ( .) и все конечные нули впоследствии в одной операции:('%f' % x).rstrip('.0')
Габриэль Стейплс

143

Вы можете использовать %gдля достижения этого:

'%g'%(3.140)

или, для Python 2.6 или выше:

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

Из документов дляformat : gпричины (среди прочего)

незначительные завершающие нули [должны быть] удалены из значимостей, а десятичная точка также удаляется, если после нее нет оставшихся цифр.


28
О, почти! Иногда он форматирует число с плавающей точкой в ​​научной нотации («2.342E + 09») - возможно ли его отключить, т.е. всегда показывать все значащие цифры?
TarGz

5
Зачем использовать, '{0:...}'.format(value)когда вы могли бы использовать format(value, '...')? Это позволяет избежать необходимости разбирать спецификатор формата из строки шаблона, которая в противном случае пуста.
Мартин Питерс

2
@MartijnPieters: минимальные затраты на разбор спецификатора формата затоплены другими накладными расходами AFAICT; на практике мои локальные тесты на 3.6 (с областью видимости функции микробенчмарка для точного моделирования реального кода) format(v, '2.5f')заняли ~ 10% дольше, чем '{:2.5f}'.format(v). Даже если это не так, я склонен использовать strформу метода, потому что, когда мне нужно настроить ее, добавить к ней дополнительные значения и т. Д., Есть меньше изменений. Конечно, с 3.6 у нас есть f-строки для большинства целей. :-)
ShadowRanger

5
В Python 3.6, это может быть сокращен до , f"{var:g}"где varпеременная с плавающей точкой.
TheGreatCabbage

12

Как насчет того, чтобы попробовать самый простой и, вероятно, самый эффективный подход? Метод normalize () удаляет все крайние правые нули.

from decimal import Decimal

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

Работает в Python 2 и Python 3 .

- Обновлено -

Единственная проблема, как указал @ BobStein-VisiBone, состоит в том, что числа, такие как 10, 100, 1000 ... будут отображаться в экспоненциальном представлении. Это можно легко исправить с помощью следующей функции:

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

2
Кроме Decimal('10.0').normalize()становится'1E+1'
Боб Стейн

11

После просмотра ответов на несколько похожих вопросов, мне кажется, это лучшее решение:

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

Мои рассуждения:

%g не избавляется от научной нотации.

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

15 знаков после запятой, кажется, избегают странного поведения и обладают достаточной точностью для моих нужд.

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

Я мог бы использовать format(inputValue, '.15f').вместо '%.15f' % inputValue, но это немного медленнее (~ 30%).

Я мог бы использовать Decimal(inputValue).normalize(), но это также имеет несколько проблем. Во-первых, это НАМНОГО медленнее (~ 11x). Я также обнаружил, что, хотя он имеет довольно большую точность, он все еще страдает от потери точности при использовании normalize().

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

Самое главное, что я все равно буду переходить Decimalс а, floatчто может заставить вас получить что-то, кроме числа, которое вы там указали. Я думаю, что Decimalлучше всего работает, когда арифметика остается Decimalи Decimalинициализируется строкой.

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

Я уверен, что проблема точности Decimal.normalize()может быть приспособлена к тому, что необходимо, используя настройки контекста, но учитывая и без того медленную скорость и не требуя смешной точности и того факта, что я все равно буду конвертировать с плавающей запятой и теряя точность в любом случае, я не Не думаю, что это стоит того, чтобы преследовать.

Меня не интересует возможный результат "-0", так как -0.0 является допустимым числом с плавающей запятой, и, возможно, это будет редким случаем в любом случае, но так как вы упомянули, что хотите сохранить строковый результат как можно более коротким, вы всегда можно использовать дополнительные условия при очень небольших дополнительных затратах на скорость.

def floatToString(inputValue):
    result = ('%.15f' % inputValue).rstrip('0').rstrip('.')
    return '0' if result == '-0' else result

1
К сожалению, работает только с числами, которые содержат менее чем примерно пять или более цифр слева от десятичного разряда. floatToString(12345.6)возвращается '12345.600000000000364'к примеру. Уменьшение 15 %.15fна более низкое число решает его в этом примере, но это значение нужно уменьшать все больше и больше, когда число становится больше. Это может быть динамически рассчитано на основе log-base-10 числа, но это быстро становится очень сложным.
JohnSpeeks

1
Одним из способов решения этой проблемы может быть ограничение длины целого числа (а не только цифр после десятичной дроби):result = ('%15f' % val).rstrip('0').rstrip('.').lstrip(' ')
Тимоти Смит

@JohnSpeeks Я не уверен, что этого можно избежать. Это побочный эффект от того, что плавающие числа не способны отображать точность, если на левой стороне требуется больше цифр. Из того, что я могу сказать, число, которое появляется как строка, является тем же самым числом, которое входит как число с плавающей запятой, или по крайней мере самое близкое его представление. >>>12345.600000000000364 == 12345.6 True
ПолиМеш

Я написал другое решение .
Ниитсума

8

Вот решение, которое сработало для меня. Это смесь из раствора путем Polymesh и использования нового .format() синтаксиса .

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

Выход :

3
3
3
3.1
3.14
3.14

Единственное, что не так с этим, это то, что вы должны установить разумное количество десятичных цифр. Чем выше вы установите его, тем более точные цифры вы сможете представить, но если вы сделаете это много, это может ухудшить производительность.
Беруик

1
В добавление к комментарию Беруика, это не работает для поплавков большей точности (например 3.141), поскольку они .2fжестко запрограммированы.
TrebledJ

3

Вы можете просто использовать format () для достижения этой цели:

format(3.140, '.10g') где 10 - точность, которую вы хотите.


2
>>> str(a if a % 1 else int(a))

Ты имеешь в виду int(a) if a % 1 else a?
Беруик

Уважаемый Беруик, ваш ответ приводит к отрицательному ответу. a if a % 1 else int(a)правильно. Вопрос нуждается в выводе в строку, поэтому я просто добавилstr
Shameem

Ах, теперь я понял. a % 1это правда, потому что это не ноль. Я безоговорочно и ошибочно воспринимал это как a % 1 == 0.
Беруик

2

В то время как форматирование, вероятно, является наиболее питонским способом, здесь есть альтернативное решение, использующее more_itertools.rstripинструмент.

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"

Число преобразуется в строку, в которой отсутствуют завершающие символы, удовлетворяющие предикату. Определение функции fmtне является обязательным, но оно используется здесь для проверки утверждений, которые все проходят. Примечание: он работает со строковыми входами и принимает необязательные предикаты.

Смотрите также подробности об этой сторонней библиотеке more_itertools.


1

Если вы можете жить с 3. и 3.0, выглядящими как «3.0», очень простой подход, который удаляет нули прямо из представлений с плавающей точкой:

print("%s"%3.140)

(спасибо @ellimilial за указание на исключения)


1
Но print("%s"%3.0)делает.
Эллимиал

1

Использование пакета QuantiPhy является опцией. Обычно QuantiPhy используется при работе с числами с единицами измерения и масштабными коэффициентами СИ, но у него есть множество приятных опций форматирования чисел.

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

И он не будет использовать электронную запись в этой ситуации:

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

Альтернативой, которую вы можете предпочесть, является использование масштабных коэффициентов СИ, возможно, с единицами измерения.

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

0

OP хотел бы удалить лишние нули и сделать полученную строку максимально короткой.

Я считаю, что экспоненциальное форматирование% g сокращает результирующую строку для очень больших и очень малых значений. Проблема возникает для значений, которые не нуждаются в экспоненциальной записи, например 128.0, которая не является ни очень большой, ни очень маленькой.

Вот один из способов форматирования чисел в виде коротких строк, который использует экспоненциальную запись% g, только когда Decimal.normalize создает слишком длинные строки. Возможно, это не самое быстрое решение (поскольку оно использует 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']


0

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

Я использую это, чтобы форматировать числа с плавающей точкой, чтобы отследить нули.


3143,93 -> 3,1e + 03
j35t3r

0

Вот ответ:

import numpy

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

вывод "3.14" и "3"

trim='-' удаляет как конечные нули, так и десятичные.


-1

Используйте% g с достаточно большой шириной, например "% .99g". Он будет печатать в фиксированной записи для любого достаточно большого числа.

РЕДАКТИРОВАТЬ: это не работает

>>> '%.99g' % 0.0000001
'9.99999999999999954748111825886258685613938723690807819366455078125e-08'

1
.99точность, а не ширина; вроде полезно, но вы не можете установить фактическую точность таким образом (кроме усечения ее самостоятельно).
Кос

-1

Вы можете использовать max()как это:

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


1
Вы должны рассмотреть случай, когда xотрицательный. if x < 0: print(min(x), x) else : print(max(x), x)
ThunderPhoenix

Полезный метод, когда я хочу сделать json stringify. float 1.0 изменяется на int 1, поэтому он работает так же, как в javascript.
pingze

-3

Вы можете достичь этого наиболее питоническим способом, вот так:

python3:

"{:0.0f}".format(num)

Ты прав. Самый простой способ - использовать формат "{: g}". Num
Pyglouthon

-4

Обработка% f, и вы должны положить

% .2f

где: .2f == .00 плавает.

Пример:

распечатать "Цена:% .2f"% цены [товар]

вывод:

Цена: 1.50


1
Это четко указано не то, что хочет вопрос. Нужно понизить голос.
эпизодян
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.