Форматування плаває без останнього нуля


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 - це те саме число з плаваючою комою. Нуль не існує в першу чергу.
С.Лотт

33
@ S.Lott - Я думаю, що проблема - ПЕЧЕННЯ числа з поплавком без прорахунків нулів, а не фактичної еквівалентності двох чисел.
pokstad

1
@pokstad: У такому випадку немає "зайвого" нуля. %0.2fі %0.3fце два формати, необхідні для створення останніх цифр зліва. Використовуйте %0.2fдля створення останніх двох цифр праворуч.
С.Лотт

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") - чи можна його вимкнути, тобто завжди показувати всі значущі цифри?
ТарГз

5
Навіщо використовувати, '{0:...}'.format(value)коли ви могли використовувати format(value, '...')? Це дозволяє уникнути розбору специфікатора формату з рядка шаблону, який інакше порожній.
Martijn Pieters

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змінної float.
TheGreatCabbage

12

А як спробувати найпростіший і, мабуть, найефективніший підхід? Метод нормалізації () видаляє всі найправіші кінцеві нулі.

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 Я не впевнений, що цього можна уникнути. Це побічний ефект плаваючих чисел, який не може представити точність, якщо на лівій стороні потрібно більше цифр. З того, що я можу сказати, число, яке виходить як рядок, - це те саме число, яке входить як float, або, принаймні, найближче його представлення. >>>12345.600000000000364 == 12345.6 True
PolyMesh

Я написав ще одне рішення .
niitsuma

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

Єдине, що не в цьому - те, що вам потрібно встановити значну кількість десяткових цифр. Чим вище ви його встановите, тим точніші цифри ви можете представляти, але якщо ви це зробите багато, це може погіршити продуктивність.
beruic

1
Додаючи до коментаря beruic, це не працює для поплавців більшої точності (наприклад 3.141), оскільки .2fжорсткий код.
TrebledJ

3

Ви можете просто скористатися форматом () для досягнення цього:

format(3.140, '.10g') де 10 - точність, яку ви хочете.


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

Ви не маєте на увазі int(a) if a % 1 else a?
beruic

Шановний Беруїк, ваша відповідь дає негативну відповідь. a if a % 1 else int(a)правильно. Питання потребує виведення в рядку, тому я щойно додавstr
Shameem

Ах, я зрозумів це зараз. a % 1є правдою, тому що вона не є нульовою. Я неявно і неправильно сприйняв це як a % 1 == 0.
beruic

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)робить.
ellimilial

1

Використання пакета QuantiPhy - це варіант. Зазвичай QuantiPhy використовується при роботі з числами з одиницями та коефіцієнтами шкали SI, але він має різноманітні варіанти приємного форматування чисел.

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

Ви можете скористатися альтернативою - використовувати фактори масштабу SI, можливо, з одиницями.

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

ОП хотів би видалити зайві нулі та зробити отриманий рядок якомога коротшим.

Я вважаю, що експоненціальне форматування% 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

Ось відповідь:

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". Він буде надрукований у фіксованій точці для будь-якої досить великої кількості.

EDIT: це не працює

>>> '%.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}". (
Число

-4

Обробка% f і вам слід поставити

% .2f

, де: .2f == .00 плаває.

Приклад:

друкувати "Ціна:% .2f"% ціни [товар]

вихід:

Ціна: 1,50


1
Це чітко сказано не те, чого хоче питання. Потрібно зняти участь.
episodeyang
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.