Ta odpowiedź na pierwsze połączone pytanie ma na końcu linię, która jest niemal wyrzucana:
Zobacz także informacje %g
o zaokrąglaniu do określonej liczby cyfr znaczących.
Więc możesz po prostu pisać
printf "%.2g" "$n"
(ale zapoznaj się z poniższą sekcją dotyczącą separatora dziesiętnego i ustawień regionalnych oraz zauważ, że non-Bash printf
nie musi obsługiwać %f
i %g
).
Przykłady:
$ printf "%.2g\n" 76543 0.0076543
7.7e+04
0.0077
Oczywiście masz teraz reprezentację mantysy-wykładnika zamiast czystego dziesiętnego, więc będziesz chciał przekonwertować:
$ printf "%0.f\n" 7.7e+06
7700000
$ printf "%0.7f\n" 7.7e-06
0.0000077
Złożenie tego wszystkiego razem i zawinięcie w funkcję:
# Function round(precision, number)
round() {
n=$(printf "%.${1}g" "$2")
if [ "$n" != "${n#*e}" ]
then
f="${n##*e-}"
test "$n" = "$f" && f= || f=$(( ${f#0}+$1-1 ))
printf "%0.${f}f" "$n"
else
printf "%s" "$n"
fi
}
(Uwaga - ta funkcja jest napisana w przenośnej (POSIX) powłoce, ale zakłada, że printf
obsługuje konwersje zmiennoprzecinkowe. Bash ma wbudowaną funkcję printf
, więc wszystko jest w porządku, a implementacja GNU również działa, więc większość GNU / Systemy Linux mogą bezpiecznie korzystać z Dasha).
Przypadki testowe
radix=$(printf %.1f 0)
for i in $(seq 12 | sed -e 's/.*/dc -e "12k 1.234 10 & 6 -^*p"/e' -e "y/_._/$radix/")
do
echo $i "->" $(round 2 $i)
done
Wyniki testu
.000012340000 -> 0.000012
.000123400000 -> 0.00012
.001234000000 -> 0.0012
.012340000000 -> 0.012
.123400000000 -> 0.12
1.234 -> 1.2
12.340 -> 12
123.400 -> 120
1234.000 -> 1200
12340.000 -> 12000
123400.000 -> 120000
1234000.000 -> 1200000
Uwaga na temat separatora dziesiętnego i ustawień regionalnych
Wszystkie powyższe prace zakładają, że znak podstawnika (znany również jako separator dziesiętny) jest .
, jak w większości angielskich lokalizacjach. ,
Zamiast tego używają innych ustawień regionalnych , a niektóre powłoki mają wbudowaną funkcję, printf
która respektuje ustawienia regionalne. W tych powłokach może być konieczne ustawienie LC_NUMERIC=C
wymuszania użycia .
znaku radix lub zapis, /usr/bin/printf
aby uniemożliwić użycie wbudowanej wersji. To ostatnie jest skomplikowane przez fakt, że (przynajmniej niektóre wersje) wydają się zawsze analizować argumenty za pomocą .
, ale drukować przy użyciu bieżących ustawień regionalnych.
%f
/%g
, ale taki jestprintf
argument i nie trzeba POSIX-a,printf
aby mieć powłokę POSIX. Myślę, że powinieneś skomentować zamiast tam edytować.printf %g
nie można używać w skrypcie POSIX. To prawdaprintf
, że to zależy od narzędzia, ale to narzędzie jest wbudowane w większość powłok. OP oznaczone jako bash, więc użycie shebang bash jest jednym łatwym sposobem na uzyskanie printf, który obsługuje% g. W przeciwnym razie musisz dodać założenie, że twoje printf (lub printf wbudowane w twojesh
if, jeśliprintf
jest tam wbudowane) obsługuje niestandardowe (ale dość powszechne)%g
...dash
ma wbudowaneprintf
(które obsługuje%g
). W systemach GNUmksh
jest to prawdopodobnie jedyna powłoka w dzisiejszych czasach, która nie będzie miała wbudowanegoprintf
.bash
) i przenieść niektóre z nich do notatek - czy teraz wygląda to poprawnie?printf "%.3g\n" 0.400
daje 0,4, a nie 0,400TL; DR
Po prostu skopiuj i użyj funkcji
sigf
z tej sekcjiA reasonably good "significant numbers" function:
. Jest napisany (jak cały kod w tej odpowiedzi) do pracy z myślnikiem .Daje
printf
przybliżenie do całkowitej liczby N za pomocą$sig
cyfr.O separatorze dziesiętnym.
Pierwszym problemem do rozwiązania za pomocą printf jest efekt i użycie „znaku dziesiętnego”, który w USA jest kropką, a w DE przecinkiem (na przykład). Jest to problem, ponieważ to, co działa dla niektórych ustawień narodowych (lub powłoki), zawiedzie w przypadku niektórych innych ustawień regionalnych. Przykład:
Jednym z powszechnych (i niepoprawnych rozwiązań) jest ustawienie
LC_ALL=C
dla polecenia printf. Ale to ustawia znak dziesiętny na stały punkt dziesiętny. W przypadku lokalizacji, w których przecinek (lub inny) jest najczęściej używanym znakiem, który stanowi problem.Rozwiązaniem jest sprawdzenie w skrypcie powłoki, w której działa, co to jest separator dziesiętny ustawień narodowych. To całkiem proste:
Usuwanie zer:
Ta wartość służy do zmiany pliku z listą testów:
Dzięki temu przebiegi w dowolnej powłoce lub ustawieniach regionalnych są automatycznie poprawne.
Niektóre podstawy.
Intuicyjne powinno być cięcie liczby, która ma zostać sformatowana za pomocą formatu,
%.*e
a nawet%.*g
printf. Główną różnicą między używaniem%.*e
lub%.*g
jest to, jak liczą cyfry. Jeden używa pełnej liczby, drugi potrzebuje liczby mniejszej 1:To działało dobrze dla 4 cyfr znaczących.
Po wycięciu liczby cyfr z liczby potrzebujemy dodatkowego kroku, aby sformatować liczby z wykładnikami innymi niż 0 (tak jak powyżej).
To działa poprawnie. Liczba części całkowitych (po lewej stronie znaku dziesiętnego) jest tylko wartością wykładnika ($ exp). Potrzebna liczba miejsc po przecinku to liczba cyfr znaczących ($ sig) pomniejszona o liczbę cyfr użytą już w lewej części separatora dziesiętnego:
Ponieważ integralna część
f
formatu nie ma ograniczeń, w rzeczywistości nie ma potrzeby jawnego deklarowania go, a ten (prostszy) kod działa:Pierwsza próba.
Pierwsza funkcja, która może to zrobić w bardziej zautomatyzowany sposób:
Ta pierwsza próba działa z wieloma liczbami, ale zakończy się niepowodzeniem z liczbami, dla których liczba dostępnych cyfr jest mniejsza niż żądana znacząca liczba, a wykładnik jest mniejszy niż -4:
Doda to wiele zer, które nie są potrzebne.
Druga próba.
Aby rozwiązać ten problem, musimy wyczyścić N wykładnika i wszelkich zer końcowych. Następnie możemy uzyskać efektywną długość cyfr i pracować z tym:
Jednak używa to matematyki zmiennoprzecinkowej i „nic nie jest proste w zmiennoprzecinkowym”: Dlaczego moje liczby się nie sumują?
Ale nic w „punkcie zmiennoprzecinkowym” nie jest proste.
Jednak:
Dlaczego?:
A także polecenie
printf
jest wbudowane w wiele powłok.Jakie
printf
wydruki mogą się zmienić wraz z powłoką:Dość dobra funkcja „liczb znaczących”:
A wyniki są następujące:
źródło
Jeśli masz już numer jako ciąg, czyli „3456” lub „0,003756”, możesz potencjalnie zrobić to tylko przy użyciu manipulacji ciągiem. To, co dzieje się poniżej mojej głowy, nie zostało dokładnie przetestowane i używa sed, ale zastanów się:
Tam, gdzie w zasadzie rozbierasz i zapisujesz jakieś „-0.000” rzeczy na początku, użyj prostej operacji podciągania na pozostałych. Jednym z powyższych zastrzeżeń jest to, że wiele wiodących zer nie jest usuwanych. Zostawię to jako ćwiczenie.
źródło