Jak zaokrąglić liczbę do cyfr znaczących w Pythonie

148

Muszę zaokrąglić liczbę zmiennoprzecinkową, aby była wyświetlana w interfejsie użytkownika. Np. Do jednej znaczącej liczby:

1234 -> 1000

0,12 -> 0,1

0,012 -> 0,01

0,062 -> 0,06

6253 -> 6000

1999 -> 2000

Czy istnieje dobry sposób na zrobienie tego za pomocą biblioteki Python, czy też muszę sam to napisać?

Peter Graham
źródło
jakich wyników oczekujesz dla 0,062 i 6253?
lamirap
Pakiet do precyzji teraz to robi. Moja opublikowana odpowiedź szczegółowo opisuje, jak to się dzieje.
William Rusnack

Odpowiedzi:

146

Możesz użyć liczb ujemnych do zaokrąglenia liczb całkowitych:

>>> round(1234, -3)
1000.0

Zatem jeśli potrzebujesz tylko najbardziej znaczącej cyfry:

>>> from math import log10, floor
>>> def round_to_1(x):
...   return round(x, -int(floor(log10(abs(x)))))
... 
>>> round_to_1(0.0232)
0.02
>>> round_to_1(1234243)
1000000.0
>>> round_to_1(13)
10.0
>>> round_to_1(4)
4.0
>>> round_to_1(19)
20.0

Prawdopodobnie będziesz musiał zadbać o zmianę liczby zmiennoprzecinkowej na liczbę całkowitą, jeśli jest większa niż 1.

Evgeny
źródło
3
To jest poprawne rozwiązanie. Użycie log10jest jedynym właściwym sposobem określenia sposobu zaokrąglenia.
Wolph
73
round_to_n = lambda x, n: round (x, -int (floor (log10 (x))) + (n - 1))
Roy Hyunjin Han,
28
Powinieneś użyć log10(abs(x)), w przeciwnym razie liczby ujemne zawiodą (i x == 0oczywiście traktuj osobno)
Tobias Kienzler
2
Stworzyłem pakiet, który robi to teraz i prawdopodobnie jest łatwiejszy i bardziej niezawodny niż ten. Opublikuj link , link do repozytorium . Mam nadzieję że to pomoże!
William Rusnack
2
round_to_n = lambda x, n: x if x == 0 else round(x, -int(math.floor(math.log10(abs(x)))) + (n - 1))chroni przed x==0i x<0dziękuję @RoyHyunjinHan i @TobiasKienzler. Nie jest chroniony przed niezdefiniowanymi, takimi jak math.inf, ani śmieciami, takimi jak None itp.
AJP
98

% g w formatowaniu łańcucha sformatuje liczbę zmiennoprzecinkową zaokrągloną do pewnej liczby cyfr znaczących. Czasami używa notacji naukowej 'e', ​​więc przekonwertuj zaokrąglony ciąg z powrotem na zmiennoprzecinkowy, a następnie przez formatowanie ciągu% s.

>>> '%s' % float('%.1g' % 1234)
'1000'
>>> '%s' % float('%.1g' % 0.12)
'0.1'
>>> '%s' % float('%.1g' % 0.012)
'0.01'
>>> '%s' % float('%.1g' % 0.062)
'0.06'
>>> '%s' % float('%.1g' % 6253)
'6000.0'
>>> '%s' % float('%.1g' % 1999)
'2000.0'
Peter Graham
źródło
7
W PO wymagano, aby rok 1999 był sformatowany jako „2000”, a nie jako „2000.0”. Nie widzę trywialnego sposobu na zmianę metody, aby to osiągnąć.
Tim Martin,
1
Właśnie tego zawsze chciałem! gdzie to znalazłeś?
djhaskin987
12
Zauważ, że zachowanie% g nie zawsze jest poprawne. W szczególności zawsze obcina zera końcowe, nawet jeśli są znaczące. Liczba 1,23400 ma 6 cyfr znaczących, ale „% .6g”% (1,23400) da w wyniku „1,234”, które jest nieprawidłowe. Więcej szczegółów w tym poście na blogu: randlet.com/blog/python-sertainant-figures-format
randlet
3
Podobnie jak w metodzie odpowiedzi Evgeny, ten nie poprawnie okrągły 0.075do 0.08. 0.07Zamiast tego wraca .
Gabriel
1
round_sig = lambda f,p: float(('%.' + str(p) + 'e') % f)pozwala dostosować liczbę cyfr znaczących!
denizb
49

Jeśli chcesz mieć inne niż 1 znaczące miejsce po przecinku (w przeciwnym razie to samo co Evgeny):

>>> from math import log10, floor
>>> def round_sig(x, sig=2):
...   return round(x, sig-int(floor(log10(abs(x))))-1)
... 
>>> round_sig(0.0232)
0.023
>>> round_sig(0.0232, 1)
0.02
>>> round_sig(1234243, 3)
1230000.0
indgar
źródło
8
round_sig (-0.0232) -> błąd domeny matematycznej, możesz chcieć dodać tam abs ();)
dgorissen
2
Podobnie jak w przypadku metod w odpowiedzi Evgeny i Peter Graham, ten nie poprawnie okrągły 0.075do 0.08. 0.07Zamiast tego wraca .
Gabriel
3
Nie udaje się również dla round_sig (0).
Yuval Atzmon,
2
@Gabriel To jest wbudowana "funkcja" języka Python działająca na twoim komputerze i przejawia się w zachowaniu funkcji round. docs.python.org/2/tutorial/floatingpoint.html#tut-fp-issues
Nowicjusz C
1
@Gabriel Dodałem odpowiedź, która wyjaśnia, dlaczego powinieneś spodziewać się zwrotu 0,7 z zaokrąglenia „0,075”! patrz stackoverflow.com/a/56974893/1358308
Sam Mason,
30
f'{float(f"{i:.1g}"):g}'
# Or with Python <3.6,
'{:g}'.format(float('{:.1g}'.format(i)))

To rozwiązanie różni się od wszystkich innych, ponieważ:

  1. to dokładnie rozwiązuje kwestię OP
  2. to jednak nie potrzeba żadnego dodatkowego pakietu
  3. to jednak nie potrzeba żadnego zdefiniowanego przez użytkownika funkcję pomocniczą lub operacji matematycznych

W przypadku dowolnej liczby ncyfr znaczących możesz użyć:

print('{:g}'.format(float('{:.{p}g}'.format(i, p=n))))

Test:

a = [1234, 0.12, 0.012, 0.062, 6253, 1999, -3.14, 0., -48.01, 0.75]
b = ['{:g}'.format(float('{:.1g}'.format(i))) for i in a]
# b == ['1000', '0.1', '0.01', '0.06', '6000', '2000', '-3', '0', '-50', '0.8']

Uwaga : w przypadku tego rozwiązania nie jest możliwe dynamiczne dostosowywanie liczby cyfr znaczących na podstawie danych wejściowych, ponieważ nie ma standardowego sposobu rozróżniania liczb z różnymi liczbami końcowych zer ( 3.14 == 3.1400). Jeśli zajdzie taka potrzeba, potrzebne są niestandardowe funkcje, takie jak te zawarte w pakiecie do precyzji .

Falken
źródło
FYI: Znalazłem to rozwiązanie niezależnie od eddygeek, gdy próbowałem rozwiązać ten sam problem w jednym z moich kodów. Teraz zdaję sobie sprawę, że moje rozwiązanie jest oczywiście prawie identyczne z jego (właśnie zauważyłem błędne wyjście i nie zawracałem sobie głowy czytaniem kodu, mój błąd). Prawdopodobnie krótki komentarz pod jego odpowiedzią wystarczyłby zamiast nowej odpowiedzi ... Jedyną (kluczową) różnicą jest podwójne użycie programu :gformatującego, który zachowuje liczby całkowite.
Falken
Wow, twoją odpowiedź trzeba naprawdę przeczytać od góry do dołu;) Ta sztuczka z podwójnym rzutem jest brudna, ale zgrabna. (Zauważ, że rok 1999 został sformatowany zgodnie z 2000.0 sugestią 5 cyfr znaczących, więc musi przejść {:g}ponownie.) Ogólnie, liczby całkowite z zerami końcowymi są niejednoznaczne w odniesieniu do cyfr znaczących, chyba że użyta jest jakaś technika (np. Podkreślenie powyżej ostatniego znaczącego).
Tomasz Gandor
8

Stworzyłem precyzyjny pakiet, który robi to, co chcesz. Pozwala na podanie liczb mniej lub bardziej znaczących.

Wyprowadza również notację standardową, naukową i inżynierską z określoną liczbą cyfr znaczących.

W zaakceptowanej odpowiedzi jest linia

>>> round_to_1(1234243)
1000000.0

To faktycznie określa 8 sig fig. Dla numeru 1234243 moja biblioteka wyświetla tylko jedną znaczącą cyfrę:

>>> from to_precision import to_precision
>>> to_precision(1234243, 1, 'std')
'1000000'
>>> to_precision(1234243, 1, 'sci')
'1e6'
>>> to_precision(1234243, 1, 'eng')
'1e6'

Zaokrągli również ostatnią znaczącą cyfrę i może automatycznie wybrać, jakiego zapisu użyć, jeśli notacja nie jest określona:

>>> to_precision(599, 2)
'600'
>>> to_precision(1164, 2)
'1.2e3'
William Rusnack
źródło
Teraz szukam tego samego, ale zastosowałem do pandy df
mhoff
@mhoff, prawdopodobnie możesz użyć mapy pandy z lambdą. lambda x: to_precision(x, 2)
William Rusnack
Dodaj to do (PyPI) [ pypi.org/] . O ile wiem, nic takiego tam nie istnieje.
Morgoth
to świetny pakiet, ale myślę, że większość funkcji znajduje się teraz w module
sigfig
1
ma błąd: std_notation (9.999999999999999e-05, 3) daje: '0,00010', czyli tylko 2 cyfry znaczące
Boris Mulder
5

Aby zaokrąglić liczbę całkowitą do 1 cyfry znaczącej, podstawową ideą jest przekonwertowanie jej na liczbę zmiennoprzecinkową z 1 cyfrą przed kropką i zaokrąglić ją, a następnie przekonwertować ją z powrotem do pierwotnej wielkości całkowitej.

Aby to zrobić, musimy znać największą potęgę 10 mniejszą od liczby całkowitej. Możemy do tego użyć floor funkcji log 10.

from math import log10, floor
def round_int(i,places):
    if i == 0:
        return 0
    isign = i/abs(i)
    i = abs(i)
    if i < 1:
        return 0
    max10exp = floor(log10(i))
    if max10exp+1 < places:
        return i
    sig10pow = 10**(max10exp-places+1)
    floated = i*1.0/sig10pow
    defloated = round(floated)*sig10pow
    return int(defloated*isign)
Cris Stringfellow
źródło
1
Plus jeden za rozwiązanie, które działa bez okrągłych znaków Pythona (.., cyfry) i bez żadnych zobowiązań!
Steve Rogers
5

Aby bezpośrednio odpowiedzieć na pytanie, oto moja wersja używająca nazewnictwa z funkcji R :

import math

def signif(x, digits=6):
    if x == 0 or not math.isfinite(x):
        return x
    digits -= math.ceil(math.log10(abs(x)))
    return round(x, digits)

Moim głównym powodem opublikowania tej odpowiedzi są komentarze narzekające, że „0,075” zaokrągla do 0,07 zamiast 0,08. Wynika to, jak wskazał „Nowicjusz C”, z połączenia arytmetyki zmiennoprzecinkowej mającej zarówno skończoną precyzję, jak i reprezentację o podstawie 2 . Liczba najbliższa 0,075, którą można w rzeczywistości przedstawić, jest nieco mniejsza, dlatego zaokrąglanie wygląda inaczej, niż można by się naiwnie spodziewać.

Należy również zauważyć, że dotyczy to wszelkich zastosowań arytmetyki zmiennoprzecinkowej innej niż dziesiętna, np. C i Java mają ten sam problem.

Aby pokazać bardziej szczegółowo, prosimy Pythona o sformatowanie liczby w formacie „szesnastkowym”:

0.075.hex()

co daje nam: 0x1.3333333333333p-4. Powodem takiego postępowania jest to, że normalna reprezentacja dziesiętna często obejmuje zaokrąglanie, a zatem nie jest to sposób, w jaki komputer faktycznie „widzi” liczbę. Jeśli nie jesteś przyzwyczajony do tego formatu, kilka przydatnych odnośników są docs Python i standard C .

Aby pokazać, jak działają te liczby, możemy wrócić do punktu wyjścia, wykonując:

0x13333333333333 / 16**13 * 2**-4

który powinien zostać wydrukowany 0.075. 16**13Dzieje się tak, ponieważ po przecinku znajduje się 13 cyfr szesnastkowych, a 2**-4wykładniki szesnastkowe mają podstawę 2.

Teraz mamy już pewne wyobrażenie o tym, jak reprezentowane są zmiennoprzecinkowe, możemy użyć decimalmodułu, aby zwiększyć precyzję, pokazując nam, co się dzieje:

from decimal import Decimal

Decimal(0x13333333333333) / 16**13 / 2**4

dawanie: 0.07499999999999999722444243844i miejmy nadzieję, że wyjaśnia, dlaczego round(0.075, 2)ocenia0.07

Sam Mason
źródło
1
To świetne wyjaśnienie, dlaczego 0,075 zaokrągla się w dół do 0,07 na poziomie kodu , ale nas (w naukach fizycznych) nauczono, aby zawsze zaokrąglać w górę, a nie w dół. Tak więc oczekiwanym zachowaniem jest w rzeczywistości wynik 0,08, pomimo problemów z precyzją zmiennoprzecinkową.
Gabriel
1
Nie jestem pewien, gdzie jest twoje zamieszanie: kiedy wprowadzasz 0,075, tak naprawdę wpisujesz ~ 0,07499 (jak wyżej), co zaokrągla zgodnie z normalnymi zasadami matematycznymi. jeśli były przy użyciu typu danych (np dziesiętnych zmiennoprzecinkowych ), które mogłyby reprezentować 0,075 to powinno rzeczywiście okrągły do 0,08
Sam Mason
Nie jestem zdezorientowany. Kiedy wpisuję 0,075, tak naprawdę wpisuję 0,075. Cokolwiek dzieje się w matematyce zmiennoprzecinkowej wewnątrz kodu, nie obchodzi mnie to.
Gabriel,
@Gabriel: A jeśli nie celowo wprowadzony 0.074999999999999999, czego można spodziewać się w tym przypadku?
Mark Dickinson,
@MarkDickinson, to zależy. Jedna cyfra znacząca: 0,07, dwie: 0,075.
Gabriel,
4
def round_to_n(x, n):
    if not x: return 0
    power = -int(math.floor(math.log10(abs(x)))) + (n - 1)
    factor = (10 ** power)
    return round(x * factor) / factor

round_to_n(0.075, 1)      # 0.08
round_to_n(0, 1)          # 0
round_to_n(-1e15 - 1, 16) # 1000000000000001.0

Miejmy nadzieję, że biorąc pod uwagę wszystkie powyższe odpowiedzi (bez możliwości ujęcia tego jako jednowierszowej lambda;)). Jeszcze nie eksplorowałem, możesz edytować tę odpowiedź:

round_to_n(1e15 + 1, 11)  # 999999999999999.9
AJP
źródło
4

Zmodyfikowałem rozwiązanie indgar, aby obsługiwać liczby ujemne i małe liczby (w tym zero).

from math import log10, floor
def round_sig(x, sig=6, small_value=1.0e-9):
    return round(x, sig - int(floor(log10(max(abs(x), abs(small_value))))) - 1)
ryan281
źródło
Dlaczego po prostu nie sprawdzić, czy x == 0? Jeśli kochasz jedno-liniowiec, po prostu return 0 if x==0 else round(...).
pjvandehaar
2
@pjvandehaar, masz rację w ogólnym przypadku i powinienem był to uwzględnić. Ponadto do obliczeń numerycznych, które muszę wykonać, czasami otrzymujemy liczby takie jak 1e-15. W naszej aplikacji chcemy, aby porównanie dwóch małych liczb (z których jedna może wynosić zero) było traktowane jako równe. Również niektórzy ludzie chcą zaokrąglić małe liczby (mogą to być 1e-9, 1e-15 lub nawet 1e-300) do zera.
ryan281
1
Ciekawy. Dzięki za wyjaśnienie. W takim razie bardzo podoba mi się to rozwiązanie.
pjvandehaar
@Morgoth To ciekawy i trudny problem. Jak zauważyłeś, wydrukowana wartość nie pokazuje 3 cyfr znaczących, ale wartość jest poprawna (np 0.970 == 0.97.). Myślę, że możesz użyć innych rozwiązań drukowania, na przykład f'{round_sig(0.9701, sig=3):0.3f}'jeśli chcesz wydrukować zero.
ryan281
3

Jeśli chcesz zaokrąglić bez angażowania ciągów, link, który znalazłem ukryty w komentarzach powyżej:

http://code.activestate.com/lists/python-tutor/70739/

wydaje mi się najlepsze. Następnie, gdy drukujesz z dowolnymi deskryptorami formatowania łańcuchów, uzyskujesz rozsądny wynik i możesz użyć reprezentacji numerycznej do innych celów obliczeniowych.

Kod pod linkiem składa się z trzech linijek: def, doc i return. Ma błąd: musisz sprawdzić eksplodujące logarytmy. Tołatwe. Porównaj dane wejściowe z sys.float_info.min. Kompletne rozwiązanie to:

import sys,math

def tidy(x, n):
"""Return 'x' rounded to 'n' significant digits."""
y=abs(x)
if y <= sys.float_info.min: return 0.0
return round( x, int( n-math.ceil(math.log10(y)) ) )

Działa dla dowolnej skalarnej wartości liczbowej, a n może być a, floatjeśli z jakiegoś powodu musisz zmienić odpowiedź. W rzeczywistości możesz przesunąć limit, aby:

sys.float_info.min*sys.float_info.epsilon

bez wywoływania błędu, jeśli z jakiegoś powodu pracujesz z małymi wartościami.

robić się śpiącym
źródło
2

Nie przychodzi mi do głowy nic, co byłoby w stanie sobie z tym poradzić po wyjęciu z pudełka. Ale jest dość dobrze obsługiwany w przypadku liczb zmiennoprzecinkowych.

>>> round(1.2322, 2)
1.23

Liczby całkowite są trudniejsze. Nie są przechowywane jako podstawa 10 w pamięci, więc ważne miejsca nie są rzeczą naturalną. Jednak implementacja, gdy są one ciągiem, jest dość trywialna.

Lub dla liczb całkowitych:

>>> def intround(n, sigfigs):
...   n = str(n)
...   return n[:sigfigs] + ('0' * (len(n)-(sigfigs)))

>>> intround(1234, 1)
'1000'
>>> intround(1234, 2)

Jeśli chcesz utworzyć funkcję obsługującą dowolną liczbę, wolałbym przekonwertować je na ciągi znaków i poszukać miejsca dziesiętnego, aby zdecydować, co zrobić:

>>> def roundall1(n, sigfigs):
...   n = str(n)
...   try:
...     sigfigs = n.index('.')
...   except ValueError:
...     pass
...   return intround(n, sigfigs)

Inną opcją jest sprawdzenie typu. Będzie to znacznie mniej elastyczne i prawdopodobnie nie będzie dobrze współgrać z innymi liczbami, takimi jak Decimalobiekty:

>>> def roundall2(n, sigfigs):
...   if type(n) is int: return intround(n, sigfigs)
...   else: return round(n, sigfigs)
Tim McNamara
źródło
Samo majstrowanie przy łańcuchach nie zaokrągli liczb. Rok 1999 zaokrąglony do 1 znaczącej liczby to 2000, a nie 1000.
Peter Graham,
Istnieje dobra dyskusja na temat tego problemu zarchiwizowana pod adresem ActiveState code.activestate.com/lists/python-tutor/70739
Tim McNamara,
2

Opublikowana odpowiedź była najlepszą dostępną odpowiedzią, gdy została udzielona, ​​ale ma szereg ograniczeń i nie daje technicznie poprawnych liczb znaczących.

numpy.format_float_positional bezpośrednio obsługuje żądane zachowanie. Poniższy fragment zwraca xliczbę zmiennoprzecinkową sformatowaną do 4 cyfr znaczących z pominięciem notacji naukowej.

import numpy as np
x=12345.6
np.format_float_positional(x, precision=4, unique=False, fractional=False, trim='k')
> 12340.
Jesień
źródło
Dokumentacja (przeniesiona do numpy.org/doc/stable/reference/generated/… ) stwierdza, że ​​ta funkcja implementuje algorytm Dragon4 (z Steele & White 1990, dl.acm.org/doi/pdf/10.1145/93542.93559 ). Daje irytujące efekty, np print(*[''.join([np.format_float_positional(.01*a*n,precision=2,unique=False,fractional=False,trim='k',pad_right=5) for a in [.99, .999, 1.001]]) for n in [8,9,10,11,12,19,20,21]],sep='\n'). Nie sprawdziłem samego Dragon4.
Rainald62
0

Wpadłem na to również, ale potrzebowałem kontroli nad typem zaokrąglania. Dlatego napisałem szybką funkcję (patrz kod poniżej), która może brać pod uwagę wartość, typ zaokrąglenia i pożądane cyfry znaczące.

import decimal
from math import log10, floor

def myrounding(value , roundstyle='ROUND_HALF_UP',sig = 3):
    roundstyles = [ 'ROUND_05UP','ROUND_DOWN','ROUND_HALF_DOWN','ROUND_HALF_UP','ROUND_CEILING','ROUND_FLOOR','ROUND_HALF_EVEN','ROUND_UP']

    power =  -1 * floor(log10(abs(value)))
    value = '{0:f}'.format(value) #format value to string to prevent float conversion issues
    divided = Decimal(value) * (Decimal('10.0')**power) 
    roundto = Decimal('10.0')**(-sig+1)
    if roundstyle not in roundstyles:
        print('roundstyle must be in list:', roundstyles) ## Could thrown an exception here if you want.
    return_val = decimal.Decimal(divided).quantize(roundto,rounding=roundstyle)*(decimal.Decimal(10.0)**-power)
    nozero = ('{0:f}'.format(return_val)).rstrip('0').rstrip('.') # strips out trailing 0 and .
    return decimal.Decimal(nozero)


for x in list(map(float, '-1.234 1.2345 0.03 -90.25 90.34543 9123.3 111'.split())):
    print (x, 'rounded UP: ',myrounding(x,'ROUND_UP',3))
    print (x, 'rounded normal: ',myrounding(x,sig=3))
drew.ray
źródło
0

Korzystanie z formatowania w nowym stylu pythona 2.6+ (ponieważ styl% jest przestarzały):

>>> "{0}".format(float("{0:.1g}".format(1216)))
'1000.0'
>>> "{0}".format(float("{0:.1g}".format(0.00356)))
'0.004'

W Pythonie 2.7+ możesz pominąć wiodące 0s.

eddygeek
źródło
Z jaką wersją Pythona? Python 3.6.3 | Anaconda, Inc. | (domyślnie 13 października 2017 r., 12:02:49) ma ten sam stary problem z zaokrąglaniem. „{0}”. Format (float („{0: .1g}”. Format (0.075))) zwraca wartość „0,07”, a nie „0,08”
Don Mclachlan,
@DonMclachlan Dodałem wyjaśnienie, dlaczego jest to oczekiwane w stackoverflow.com/a/56974893/1358308
Sam Mason,
0

Ta funkcja wykonuje normalną rundę, jeśli liczba jest większa niż 10 ** (- decimal_positions), w przeciwnym razie dodaje więcej miejsc dziesiętnych, aż zostanie osiągnięta liczba znaczących miejsc dziesiętnych:

def smart_round(x, decimal_positions):
    dp = - int(math.log10(abs(x))) if x != 0.0 else int(0)
    return round(float(x), decimal_positions + dp if dp > 0 else decimal_positions)

Mam nadzieję, że to pomoże.

Ettore Galli
źródło
0

https://stackoverflow.com/users/1391441/gabriel , czy poniższe informacje dotyczą Twojego problemu dotyczącego rnd (.075, 1)? Caveat: zwraca wartość w postaci liczby zmiennoprzecinkowej

def round_to_n(x, n):
    fmt = '{:1.' + str(n) + 'e}'    # gives 1.n figures
    p = fmt.format(x).split('e')    # get mantissa and exponent
                                    # round "extra" figure off mantissa
    p[0] = str(round(float(p[0]) * 10**(n-1)) / 10**(n-1))
    return float(p[0] + 'e' + p[1]) # convert str to float

>>> round_to_n(750, 2)
750.0
>>> round_to_n(750, 1)
800.0
>>> round_to_n(.0750, 2)
0.075
>>> round_to_n(.0750, 1)
0.08
>>> math.pi
3.141592653589793
>>> round_to_n(math.pi, 7)
3.141593
Don Mclachlan
źródło
0

Zwraca ciąg, dzięki czemu wyniki bez części ułamkowych, a małe wartości, które w przeciwnym razie pojawiłyby się w notacji E, są wyświetlane poprawnie:

def sigfig(x, num_sigfig):
    num_decplace = num_sigfig - int(math.floor(math.log10(abs(x)))) - 1
    return '%.*f' % (num_decplace, round(x, num_decplace))
Gnubie
źródło
0

Biorąc pod uwagę tak wyczerpującą odpowiedź, dlaczego nie dodać kolejnego

To trochę lepiej pasuje do mojej estetyki, chociaż wiele z powyższych jest porównywalnych

import numpy as np

number=-456.789
significantFigures=4

roundingFactor=significantFigures - int(np.floor(np.log10(np.abs(number)))) - 1
rounded=np.round(number, roundingFactor)

string=rounded.astype(str)

print(string)

Działa to dla pojedynczych liczb i tablic numpy i powinno działać dobrze dla liczb ujemnych.

Jest jeszcze jeden krok, który możemy dodać - np.round () zwraca liczbę dziesiętną, nawet jeśli zaokrąglona jest liczbą całkowitą (tj. Dla importantFigures = 2 możemy spodziewać się powrotu -460, ale zamiast tego otrzymamy -460.0). Możemy dodać ten krok, aby to poprawić:

if roundingFactor<=0:
    rounded=rounded.astype(int)

Niestety, ten ostatni krok nie zadziała dla szeregu liczb - zostawię to tobie drogi czytelniku, aby dowiedzieć się, czy potrzebujesz.

zefir
źródło
0

Sigfig pakiet / Osłony biblioteczne to. Po zainstalowaniu możesz wykonać następujące czynności:

>>> from sigfig import round
>>> round(1234, 1)
1000
>>> round(0.12, 1)
0.1
>>> round(0.012, 1)
0.01
>>> round(0.062, 1)
0.06
>>> round(6253, 1)
6000
>>> round(1999, 1)
2000
HyperActive
źródło
0
import math

  def sig_dig(x, n_sig_dig):
      num_of_digits = len(str(x).replace(".", ""))
      if n_sig_dig >= num_of_digits:
          return x
      n = math.floor(math.log10(x) + 1 - n_sig_dig)
      result = round(10 ** -n * x) * 10 ** n
      return float(str(result)[: n_sig_dig + 1])


    >>> sig_dig(1234243, 3)
    >>> sig_dig(243.3576, 5)

        1230.0
        243.36
LetzerWille
źródło