Dlaczego istnieje tak wiele typów liczbowych (bit, int, float, double, long)?

9

Nauczyłem się PHP, Java i C. Teraz jestem ciekawy, dlaczego istnieje tak wiele rodzajów liczbowych typów danych, takich jak bit, int, float, double i long. Dlaczego nie zrobić tylko jednego typu dla liczb?

Czy ma to jakąś korzyść? Może jeśli użyjemy liczb całkowitych do przechowywania tak małych liczb, możemy zaoszczędzić pamięć?

GusDeCooL
źródło
6
Oprócz odpowiedzi HorusKola: typy „float” i „integer” są z natury różne. Liczby zmiennoprzecinkowe mogą zawierać bardzo duże liczby, ale wraz ze wzrostem liczby wzrasta precyzja. Ta niedokładność wynika ze sposobu przechowywania pływaków. Natomiast zakres wartości, które można przechowywać w liczbach całkowitych, jest dość ograniczony, ale wartość jest zawsze dokładna, dzięki czemu można znacznie łatwiej porównywać wartości. Istnieją również dwa różne typy zachowania z podziałem - liczby całkowite „obcinają” automatycznie do najbliższej liczby całkowitej, zmienne nie. Każde z tych zachowań jest przydatne w różnych sytuacjach.
kampu
JavaScript ma tylko jeden typ liczby na powierzchni.
Esailija,
@kampu: W rzeczywistości w wielu językach liczby całkowite mogą przechowywać dowolną liczbę, o ile pamięć (wirtualna) jest wystarczająco duża, aby ją reprezentować.
Jörg W Mittag
1
@ JörgWMittag: Jednak pytający wyraźnie mówi o językach statycznych, a nie dynamicznych, takich jak na przykład Python. Sam CPython implementuje liczbę całkowitą „nieograniczonego zakresu” jako tablicę liczb całkowitych 32-bitowych, a końcowy bit w każdej int służy do wskazania, czy jest więcej bitów do przejścia. Ponadto liczby całkowite mogą przechowywać tylko dowolną liczbę całkowitą . Oznacza to, że liczba zmiennoprzecinkowa z pamięcią nieskończoną może przechowywać wartości z dokładnością (nieskończoność alepha), podczas gdy liczby całkowite mogą przechowywać wartości tylko z precyzją ( nieskończoność aleph zero ).
kampu
@kampu: Ponieważ wszystkie liczby są reprezentowane przez serię bitów, nawet przy nieskończonej pamięci, zawsze będzie mapowanie jeden na jeden między liczbami zmiennoprzecinkowymi a liczbami całkowitymi. Więc nie sądzę, żeby aleph zadawał pytanie.
PRZYJDŹ

Odpowiedzi:

17

Istnieją dwa powody, dla których powinieneś zwracać uwagę na różne typy danych liczbowych.

1. Oszczędzanie pamięci

for(long k=0;k<=10;k++)
{
    //stuff
}

Po co używać długiej, gdy równie łatwo może być liczbą całkowitą, a nawet bajtem? W ten sposób można zaoszczędzić kilka bajtów pamięci.

2. Liczby zmiennoprzecinkowe i liczby całkowite są przechowywane w komputerze inaczej

Załóżmy, że mamy liczbę 22 zapisaną w liczbie całkowitej. Komputer przechowuje ten numer w pamięci binarnie jako:

0000 0000 0000 0000 0000 0000 0001 0110

Jeśli nie znasz systemu liczb binarnych, można to przedstawić w notacji naukowej jako: 2 ^ 0 * 0 + 2 ^ 1 * 1 + 2 ^ 2 * 1 + 2 ^ 3 * 0 + 2 ^ 4 * 1 + 2 ^ 5 * 0 + ... + 2 ^ 30 * 0. Ostatni bit może, ale nie musi być użyty do wskazania, czy liczba jest ujemna (w zależności od tego, czy typ danych jest podpisany czy niepodpisany).

Zasadniczo jest to suma wartości 2 ^ (bit place) *.

Zmienia się to w przypadku wartości z przecinkiem dziesiętnym. Załóżmy, że masz liczbę 3,75 po przecinku. Jest to określane binarnie jako 11.11. Możemy to przedstawić jako notację naukową jako 2 ^ 1 * 1 + 2 ^ 0 * 1 + 2 ^ -1 * 1 + 2 ^ -2 * 1 lub, znormalizowany, jako 1.111 * 2 ^ 2

Komputer nie może tego jednak zapisać: nie ma wyraźnej metody wyrażenia tego punktu binarnego (wersja dziesiętna w systemie liczb binarnych). Komputer może przechowywać tylko 1 i 0. W tym miejscu pojawia się zmiennoprzecinkowy typ danych.

Zakładając, że sizeof (liczba zmiennoprzecinkowa) wynosi 4 bajty, wtedy w sumie masz 32 bity. Pierwszy bit ma przypisany „bit znaku”. Nie ma pływających lub podwójnych niepodpisanych. Kolejnych 8 bitów jest używanych jako „wykładnik”, a ostatnie 23 bity są używane jako „znaczący” (lub czasami określany jako mantysa). Na przykładzie 3,75 nasz wykładnik wyniósłby 2 ^ 1, a nasze znaczenie wyniósłoby 1,111.

Jeśli pierwszy bit to 1, liczba jest ujemna. Jeśli nie, pozytywne. Wykładnik jest modyfikowany przez coś, co nazywa się „odchyleniem”, więc nie możemy po prostu zapisać „0000 0010” jako wykładnika. Odchylenie dla liczby zmiennoprzecinkowej pojedynczej precyzji wynosi 127, a odchylenie dla podwójnej precyzji (tutaj nazwa podwójnego typu danych ma swoją nazwę) wynosi 1023. 23 ostatnie bity są zarezerwowane dla znaczenia. Znaczenie to po prostu wartości PRAWO naszego punktu binarnego.

Naszym wykładnikiem będzie odchylenie (127) + wykładnik (1) lub przedstawione w postaci binarnej

1000 0000

Nasze znaczenie to:

111 0000 0000 0000 0000 0000

Dlatego 3,75 jest reprezentowane jako:

0100 0000 0111 0000 0000 0000 0000 0000

Teraz spójrzmy na liczbę 8 reprezentowaną jako liczba zmiennoprzecinkowa i jako liczba całkowita:

0100 0001 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 1000

Jak na świecie komputer doda 8,0 i 8? Lub nawet pomnóż je !? Komputer (a dokładniej komputery x86) ma różne części procesora, które dodają liczby zmiennoprzecinkowe i liczby całkowite.

cpmjr123
źródło
3
3) choć rzadko problem: operacje na liczbach większych niż rozmiar komputera są wolniejsze.
Loren Pechtel,
6

Wcześniej, zanim mieliśmy systemy gigabajtowe (lub na nowoczesnych systemach wbudowanych, takich jak Arduino), pamięć była na wagę złota, dlatego wdrożono skrócone metody określania, ile pamięci zajmie określona liczba - BIT jest prosty - początkowo zajmowałby tylko 1 bit pamięciowy.

Inne rozmiary danych i nazwy różnią się w zależności od systemu. W systemie 32-bitowym INT (lub MEDIUMINT) będzie na ogół 2 bajty, LONGINT będzie 4 bajty, a SMALLINT będzie jednym bajtem. Systemy 64-bitowe mogą mieć LONGINT ustawiony na 8 bajtów.

Nawet teraz - szczególnie w aplikacjach baz danych lub programach, które mają wiele instancji uruchomionych na serwerach (takich jak skrypty po stronie serwera na stronach internetowych) - powinieneś uważać na to, co wybierzesz. Wybranie liczby całkowitej o szerokości 2, 4 lub 8 bajtów do przechowywania wartości od 0 do 100 (które mogą zmieścić się w jednym bajcie) jest niezwykle marnotrawstwem, jeśli masz tabelę bazy danych z milionami rekordów.

Więcej informacji: https://en.wikipedia.org/wiki/Integer_(computer_science)

HorusKol
źródło
ładna odpowiedź +1.
Vinay,
7
Nie tylko „wcześniej”, ale także „teraz, gdy system jest mały”. Na urządzeniu wielkości Arduino trzeba być oszczędnym.
9000
1
Który system użył tylko 1 bit do przechowywania? bitów zwykle nie można bezpośrednio adresować
jk.
1
to prawda w wielu architekturach - ale bity były bezpośrednio adresowalne w naprawdę starych systemach, a nawet w niektórych nowszych systemach osadzonych (niektóre kontrolery, które zaprogramowałem 10 lat temu, działały z bitami - te miały tylko około 64 adresowalnych lokalizacji o określonych szerokościach). Wydaje mi się, że w dzisiejszych czasach kompilatory to opracowują i umieszczają w tablicach bajtowych.
HorusKol
Myślę, że nadrzędnym czynnikiem jest zdolność i wydajność procesora, a nie problemy z pamięcią
James
4

Oprócz doskonałych argumentów cpmjr123 na temat niedoboru pamięci oraz kompromisów w zakresie precyzji i zasięgu, terapia może potencjalnie również kompromis w zakresie procesora.

Większość nowoczesnych maszyn ma specjalny sprzęt do wykonywania operacji zmiennoprzecinkowych zwany FPU. Istnieją również systemy, które nie mają FPU (obecnie są to zazwyczaj małe urządzenia osadzone), w związku z czym, w zależności od docelowego sprzętu, albo nie musiałbyś wcale używać typów zmiennoprzecinkowych, albo używać programowej biblioteki zmiennoprzecinkowej. Nawet jeśli twoja maszyna ma FPU, historycznie istniały różnice w zakresie funkcji, które mogła ona zapewnić. Wszelkie funkcje niewykonane sprzętowo musiałyby być wykonane programowo (lub pominięte)

Wykonywanie obliczeń zmiennoprzecinkowych w oprogramowaniu polega na wykonywaniu wielu prostszych operacji obsługiwanych przez sprzęt. W ten sposób zyskujesz również potencjalną kompromis prędkości.

jk.
źródło
4

Być może najważniejsze jest to, że tak naprawdę istnieją trzy różne podstawowe typy liczb.

liczba całkowita, stała liczba dziesiętna i zmiennoprzecinkowa.

Wszystkie zachowują się inaczej.

Prosta operacja, taka jak 7/2, może dać odpowiedzi 3, 3,50 i 3,499 w zależności od zastosowanego typu danych.

„fixed decimal” to typ Kopciuszka, jest obsługiwany natywnie w kilku językach, takich jak COBOL i VisualBasic. Nie ma większego zainteresowania dla informatyków, ale jest niezbędny dla każdego, kto przesyła zestaw kont lub oblicza podatek od sprzedaży na fakturze.

James Anderson
źródło
Oddzieliłem je inaczej: liczby dyskretne, liczby przybliżone i zawijanie pierścieni algebraicznych. Typowymi przykładami C będzie int, floati unsigned int, odpowiednio. Typy punktów stałych są podkategorią typów dyskretnych, ale pierścienie algebraiczne zasadniczo różnią się od liczb [zamieszanie dotyczące niepodpisanych typów w C wynika z faktu, że w większości zachowują się one jak pierścienie, a nie liczby, ale nie są całkiem spójne] .
supercat
3

Czy przynoszą jakieś korzyści?

Oczywiście. Są korzyści. W świecie komputerów pamięć jest jedną z najważniejszych rzeczy do rozważenia. Jaki jest pożytek z posiadania pamięci 2kb, gdy dane mieszczą się w mniej niż 1kb? . Powinny istnieć optymalizacje. Jeśli użyjesz więcej pamięci, to oczywiście zabija prędkość twojego komputera. Czy naprawdę to lubisz? Bez prawa...?

int - 2 bytes (16 bits)

long - 4 bytes (32 bits)

long long - 8 bytes (64 bits)

float - 4 bytes

Nie tylko pamięć, ale także organizacja typu liczb. dla instancji zmiennoprzecinkowej. Precyzja ma duże znaczenie i oczywiście powinniśmy mieć jeden typ, który może dać nam większą precyzję.

Jeśli weźmiemy pod uwagę dawne czasy, mieliśmy mniej pamięci, jak zapewne wiesz. Aby go zapisać i mądrze z niego korzystać, mieliśmy te różnice. I wiele więcej, jeśli po prostu spróbujesz przeszukać google. Mam nadzieję, że to pomoże.

Vinay
źródło
3

liczby całkowite i liczby rzeczywiste (zmiennoprzecinkowe, podwójne) są koncepcyjnie różnymi typami o różnych zestawach operacji i właściwościach wewnętrznych.

Liczby całkowite są policzalne, ale zmienne nie są itp.

W rzeczywistości liczba zmiennoprzecinkowa / liczba podwójna to struktura, która łączy dwa pola liczb całkowitych: mantysę i wykładnik potęgi. Liczby zespolone (które zostały wyłączone z analizy) są jeszcze bardziej, cóż, złożone.

Każdy praktyczny język powinien mieć co najmniej liczby całkowite i zmiennoprzecinkowe jako odrębne typy - zbyt różne operacje na nich.

c-uśmiech
źródło
Nie znam wspomnianych „liczb zespolonych”. Czy możesz wyjaśnić dalej?
cpmjr123
Mam świadomość liczb zespolonych w postaci + + bi. Poprosiłem o więcej informacji o tym, jak komputer przechowuje liczby zespolone. Według mojej wiedzy nie ma prymitywnych typów danych, które to obsługują.
cpmjr123
Liczby zespolone są zwykle przechowywane jako dwie wartości zmiennoprzecinkowe, a mianowicie ich a(część rzeczywista) i b(część urojona). Procesor zwykle nie implementuje natywnej obsługi operacji na liczbach zespolonych, chociaż procesor może implementować przyspieszone instrukcje wielokrotnego dodawania operacji na parach wartości, takich jak (a b + c d) i (a b-c d).
rwong
1
Ponadto wiele języków ma pewne typy, których zachowanie jest w dużej mierze zdefiniowane jako zawijanie pierścienia algebraicznego (np. Jeśli zmienna typu uint16_tma wartość 65535, zwiększenie jej spowoduje, że będzie mieć wartość 0). Idealnie byłoby, gdyby języki miały czysto oddzielne typy reprezentujące zawijanie pierścieni algebraicznych i liczb (pozwalając na przechwytywanie liczb, które są przepełnione, a jednocześnie umożliwiając kodowi łatwe wykonywanie operacji na rzeczach, które powinny się zawijać).
supercat
-1

Oprócz tego, że typy zmiennoprzecinkowe zachowują się zupełnie inaczej niż typy całkowite, chcę podać bardziej ekstremalny przykład, dlaczego rozmiar na liczbę naprawdę ma znaczenie.

Wyobraź sobie, że chcesz posortować (długą) tablicę. Na przykład w C:

int numbers[100000000];

Mamy tutaj 100 milionów liczb.

Jeśli każda liczba ma tylko jeden bajt (więc używaj unsigned charzamiastint ), to potrzebuje 100 milionów bajtów miejsca.

Jeśli użyjesz double , to zwykle jest to 8 bajtów na liczbę, czyli 800 milionów bajtów miejsca.

Dlatego za każdym razem, gdy operujesz wieloma obiektami (liczbami w tym przykładzie), rozmiar na obiekt (rozmiar na liczbę w tym przykładzie) naprawdę ma znaczenie.

Ingo Blackman
źródło