Chcę używać dc
do obsługi niektórych podstawowych liczb 16 z punktami szesnastkowymi, ale mam problemy z precyzją. Na przykład poniżej mnożę F423F.FD
przez 100
oba hex. Oczekiwaną odpowiedzią jest F423FFD
, zamiast tego F423FFA.E1
, ścisłe, ale niewystarczające nawet po zaokrągleniu.
$ dc
16 d i o F423F.FD 100 * p
F423FFA.E1
Przeczytałem, że dc
był to kalkulator o nieograniczonej precyzji i pod żadnym względem nie jest to duża liczba. Czy coś robię źle?
Dziękuję za odpowiedzi. Biorąc pod uwagę problemy dc
, ugryzłem kulę i napisałem własny parser dla liczb rzeczywistych w innych bazach. Jeśli ktoś jest zainteresowany kodem, mogę go opublikować tutaj.
dc
aby użyć, a następnie po prostu napisać parser bezpośrednio! (Dane wejściowe mogą, ale nie muszą być dziesiętne, i mogą być w innych bazach, więc ilość dopełnienia jest różna.)dc
odpowiedziały: Prawidłowe postępowanie z cyframi ułamkowymi dziesiętnymi wymagałoby zupełnie innego modelu niż model w skali dziesiętnej używany przez dc i bc (tak jak narzuca POSIX dla bc i zgodnie z tradycją historyczną dla obu). , więc technicznie można to naprawićdc
, ale prawdopodobnie się zepsujebc
, tak sklasyfikowane jako WONTFIX.Wyrażony jako dziesiętny (przy użyciu
dc
do konwersji) odpowiada 999999.98 (w zaokrągleniu w dół) × 256, tj. 255999994.88, czyli F423FFA.E1 w systemie szesnastkowym.Różnica wynika więc z
dc
zachowania zaokrąglania: zamiast obliczać 256 × (999999 + 253 ÷ 256), co dałoby 255999997, zaokrągla 253 ÷ 256 w dół i mnoży wynik.dc
jest dowolnym kalkulatorem precyzji, co oznacza, że może obliczyć dowolną dokładność, ale musisz powiedzieć, co to jest. Domyślnie jego dokładność wynosi 0, co oznacza, że dzielenie tworzy tylko wartości całkowite, a mnożenie używa liczby cyfr na wejściu. Aby ustawić precyzję, użyjk
(i pamiętaj, że precyzja jest zawsze wyrażona cyframi dziesiętnymi, niezależnie od podstawki wejściowej lub wyjściowej):(Dokładność 8 cyfr byłaby wystarczająca, ponieważ właśnie to należy przedstawić w zakresie 1 ÷ 256 w systemie dziesiętnym.)
źródło
k
jest ustawiony:10 k 16 d i o F423F.FD p
→F423F.FA
więc musiałbym skalować wszystkie liczby przed ich użyciemdc
. Zasadniczo sprowadza się to do ich wstępnej analizy.dc
skaluje dane wejściowe tylko przy użyciu liczby cyfr, co wydaje mi się błędem (ponieważ liczba cyfr jest obliczana przy użyciu podstawki wejściowej, ale stosowana do wartości dziesiętnej).bc
którego,dc
jest oparte): „Obliczenia wewnętrzne powinny być przeprowadzane jak w systemie dziesiętnym, niezależnie od podstaw wejściowych i wyjściowych, z określoną liczbą cyfr dziesiętnych.”20 k 16 d i o 0.3 1 / p
(który drukuje .1999999999999999999). Zrozumieć, że operacja jest właśnie dzielenie0.2
przez1
(co teoretycznie nie powinno zmienić wartość). Podczas20 k 16 d i o 0.3000 1 / p
(prawidłowego) drukowania.30000000000000000
. (Problem
Problemem jest sposób, w jaki dc (i bc) rozumieją stałe numeryczne.
Na przykład wartość (szesnastkowa)
0.3
(podzielona przez 1) zostaje przekształcona w wartość zbliżoną do0.2
W rzeczywistości zmienia się
0.3
również zwykła stała :Wydaje się, że jest to dziwne, ale nie jest (więcej później).
Dodanie większej liczby zer powoduje, że odpowiedź zbliża się do poprawnej wartości:
Ostatnia wartość jest dokładna i pozostanie dokładna bez względu na to, jak można dodać więcej zer.
Problem występuje również w bc:
Jedna cyfra na bit?
Bardzo nieintuicyjny fakt w przypadku liczb zmiennoprzecinkowych polega na tym, że wymagana liczba cyfr (po kropce) jest równa liczbie bitów binarnych (również po kropce). Liczba binarna 0,101 jest dokładnie równa 0,625 po przecinku. Liczba binarna 0,0001110001 jest (dokładnie) równa
0.1103515625
(dziesięć cyfr dziesiętnych)Również dla liczby zmiennoprzecinkowej, takiej jak 2 ^ (- 10), która w systemie binarnym ma tylko jeden (ustawiony) bit:
Ma taką samą liczbę cyfr binarnych
.0000000001
(10) jak cyfry dziesiętne.0009765625
(10). Może się tak nie zdarzyć w innych bazach, ale podstawa 10 to wewnętrzna reprezentacja liczb zarówno w dc, jak i bc, a zatem jest to jedyna podstawa, o którą naprawdę musimy dbać.Dowód matematyczny znajduje się na końcu tej odpowiedzi.
skala BC
Liczbę cyfr po kropce można policzyć za pomocą wbudowanej funkcji
scale()
bc:Jak pokazano, 2 cyfry są niewystarczające do przedstawienia stałej
0.FD
.Również samo zliczanie liczby znaków użytych po kropce jest bardzo niepoprawnym sposobem zgłaszania (i używania) skali liczby. Skala liczby (w dowolnej bazie) powinna obliczyć liczbę potrzebnych bitów.
Cyfry binarne w kodzie szesnastkowym.
Jak wiadomo, każda cyfra szesnastkowa używa 4 bitów. Dlatego każda cyfra szesnastkowa po kropce dziesiętnej wymaga 4 cyfr binarnych, które ze względu na (nieparzysty?) Powyżej również wymagają 4 cyfr dziesiętnych.
Dlatego liczba taka
0.FD
będzie wymagać prawidłowej reprezentacji 8 cyfr dziesiętnych:Dodaj zera
Matematyka jest prosta (dla liczb szesnastkowych):
h
) po kropce.h
przez 4.h×4 - h = h × (4-1) = h × 3 = 3×h
zera.W kodzie powłoki (dla sh):
Który wydrukuje (poprawnie zarówno w DC, jak i BC):
Wewnętrznie bc (lub dc) może sprawić, że wymagana liczba cyfr będzie zgodna z liczbą obliczoną powyżej (
3*h
) w celu konwersji liczb szesnastkowych na wewnętrzną reprezentację dziesiętną. Lub jakąś inną funkcję dla innych baz (przy założeniu, że liczba cyfr jest skończona w stosunku do bazy 10 (wewnętrznej dla bc i dc) w takiej innej bazie). Jak 2 i (2,4,8,16, ...) i 5,10.posix
Specyfikacja posix stwierdza, że (dla bc, na którym opiera się dc):
Ale „… określona liczba cyfr dziesiętnych”. może być rozumiane jako „… potrzebna liczba cyfr dziesiętnych do przedstawienia stałej liczbowej” (jak opisano powyżej) bez wpływu na „wewnętrzne obliczenia dziesiętne”
Bo:
bc tak naprawdę nie używa 50 („określonej liczby cyfr dziesiętnych”), jak określono powyżej.
Tylko jeśli podzielony jest konwertowany (nadal niepoprawnie, ponieważ używa skali 2 do odczytu stałej
0.FD
przed rozwinięciem jej do 50 cyfr):Jest to jednak dokładne:
Ponownie, czytanie ciągów liczbowych (stałych) powinno używać poprawnej liczby bitów.
Dowód matematyczny
W dwóch krokach:
Ułamek binarny można zapisać jako a / 2 n
Ułamek binarny jest skończoną sumą ujemnych potęg dwóch.
Na przykład:
= 0 + 0 x 2 -1 + 0 x 2 -2 + 1 x 2 -3 + 1 x 2 -4 + 0 x 2 -5 + 1 x 2 -6 + 0 x 2 -7 + 1 x 2 -8 + 1 × 2–9 + 0 × 2–10 + 1 × 2–11
= 2-3 + 2-4 + 2-6 + 2-8 + 2-9 + 2-11 = (z usuniętymi zerami)
W binarnej części n bitów ostatni bit ma wartość 2 -n lub 1/2 n . W tym przykładzie: 2–11 lub 1/2 11 .
= 1/2 3 + 1/2 4 + 1/2 6 + 1/2 8 + 1/2 9 + 1/2 11 = (z odwrotnością)
Ogólnie rzecz biorąc, może się mianownika 2 n o dodatniej licznik wykładnik dwóch. Wszystkie warunki można następnie połączyć w jedną wartość a / 2 n . W tym przykładzie:
= 2, 8 /2 11 + 2 7 /2 11 + 2 5 /2 11 + 2 3 /2 11 + 2 2 /2 11 + 1/2 11 = (wyrażone 2 11 )
= (2 8 + 2 7 + 2 5 + 2 3 + 2 2 + 1) / 2 11 = (wyodrębnianie wspólnego czynnika)
= (256 + 128 + 32 + 8 + 4 + 1) / 2 11 = (przeliczone na wartość)
= 429/2 11
Każda frakcja binarna może być wyrażona jako b / 10 n
Pomnóż a / 2 n przez 5 n / 5 n , otrzymując (a × 5 n ) / (2 n × 5 n ) = (a × 5 n ) / 10 n = b / 10 n , gdzie b = a × 5 n . Ma n cyfr.
Na przykład mamy:
(429 · 5 11 ) / 10 11 = 20947265625/10 11 = 0,20947265625
Wykazano, że każda ułamek dwójkowy jest ułamkiem dziesiętnym o tej samej liczbie cyfr.
źródło