Dlaczego Java nie obsługuje podpisów int?

374

Dlaczego Java nie obsługuje obsługi liczb całkowitych bez znaku?

Wydaje mi się, że jest to dziwne pominięcie, biorąc pod uwagę, że pozwalają one na napisanie kodu, który rzadziej powoduje przepełnienie na nieoczekiwanie dużych danych wejściowych.

Co więcej, używanie liczb całkowitych bez znaku może być formą samokontroli, ponieważ wskazują one, że wartość, którą intencja miała podpisać, nigdy nie powinna być ujemna.

Wreszcie w niektórych przypadkach liczby całkowite bez znaku mogą być bardziej wydajne w przypadku niektórych operacji, takich jak dzielenie.

Jakie są wady ich włączenia?

dsimcha
źródło
137
Nie wiem, ale to mnie denerwuje; na przykład o wiele trudniej jest napisać kod sieci w ten sposób.
Tamas Czinege
20
Chciałbym, żeby były tylko dwa typy w języku / bazie danych / ... world: number and string :)
Liao
5
Pisanie kodu sieciowego wcale nie jest trudniejsze. BTW InputStream.read (), zwraca bajt bez znaku, na przykład nie podpisany, więc przykład sieci to zamieszanie IMHO. Jedyne mylące jest to, że zakładasz, że zapisanie wartości podpisanej różni się od zapisu wartości niepodpisanej. tzn. jeśli tak naprawdę nie wiesz, co się dzieje na poziomie bajtów.
Peter Lawrey
19
@ZachSaw - Zrobiłem też podwójny uraz, kiedy zobaczyłem, że projektant języka napisał ten cytat. Nie ma nic prostszego niż liczba całkowita bez znaku. Podpisane liczby całkowite są skomplikowane. Zwłaszcza, gdy weźmie się pod uwagę to, że trochę kręci się na poziomie tranzystora. A jak zmienia się podpisana liczba całkowita? Musiałem dojść do wniosku, że projektant Java ma poważny problem ze zrozumieniem logiki logicznej.
PP.
8
Dla mnie trudniej jest wykonać jakiekolwiek przetwarzanie obrazu, w którym obrazy bytenie są w stanie zapewnić prostego 140poziomu szarości, ale -116trzeba & 0xffuzyskać odpowiednią wartość.
Matthieu

Odpowiedzi:

193

Oto wywiad z Goslingiem i innymi na temat prostoty:

Gosling: Dla mnie jako projektanta języka, którego tak naprawdę nie liczę jako dzisiejszego, to, co „proste” naprawdę skończyło się na tym, to, że mógłbym oczekiwać, że J. Random Developer utrzyma specyfikację w swojej głowie. Ta definicja mówi, że na przykład Java nie jest - i w rzeczywistości wiele z tych języków ma wiele przypadków narożnych, rzeczy, których nikt tak naprawdę nie rozumie. Zapytaj dowolnego programistę C o niepodpisane, a wkrótce odkryjesz, że prawie żaden programista C tak naprawdę nie rozumie, co się dzieje z niepodpisanym, czym jest arytmetyka bez znaku. Takie rzeczy sprawiły, że C był złożony. Część językowa Java jest, jak sądzę, dość prosta. Biblioteki, które musisz wyszukać.

Uri
źródło
222
Będę musiał się nie zgodzić z Goslingiem, podając konkretny przykład (nie mniej niż CLR). Co jest jeszcze bardziej mylącego, gdy podaje się tablicy wartość liczby całkowitej ze znakiem lub długości bez znaku? Nie jest możliwe, aby tablica miała ujemną długość, ale nasz interfejs API wskazuje, że jest to możliwe.
JaredPar
18
Argument uproszczenia Javy jest częścią tego, co wpędziło nas w cały bałagan z brakiem szablonów, które ostatecznie wprowadzili do języka, ponieważ alternatywy były tak kłopotliwe. Myślę jednak, że można wspierać niepodpisane ints z odpowiednią klasą, nie potrzebuje prymów
Uri
59
Jeśli Java potrzebuje liczb całkowitych bez znaku, ponieważ indeksy tablicowe nie mogą być ujemne, potrzebuje również podzakresów (a la Pascal), ponieważ indeks tablicy nie może być większy niż rozmiar tablicy.
Wayne Conrad
81
Okej, po prostu powiedział o zaletach braku posiadania niepodpisanych typów. Teraz policzmy wady ...
Moshe Revah
83
Wolę prostotę kodu niż prostotę języka. Dlatego nienawidzę Javy.
Pijusn
50

Czytając między wierszami, myślę, że logika wyglądała mniej więcej tak:

  • ogólnie projektanci Java chcieli uprościć repertuar dostępnych typów danych
  • do codziennych celów uważali, że najczęstszą potrzebą są podpisane typy danych
  • do implementacji niektórych algorytmów czasami wymagana jest arytmetyka bez znaku, ale programiści, którzy wdrażaliby takie algorytmy, mieliby również wiedzę, aby „obejść” wykonywanie arytmetyki bez znaku z podpisanymi typami danych

Przeważnie powiedziałbym, że była to rozsądna decyzja. Być może miałbym:

  • uczyniono bajt niepodpisanym lub przynajmniej zapewnił podpisane / niepodpisane alternatywy, być może o różnych nazwach, dla tego jednego typu danych (sprawienie, że podpisanie jest dobre dla spójności, ale kiedy kiedykolwiek potrzebujesz podpisanego bajtu?)
  • skończyło się na „short” (kiedy ostatnio używałeś arytmetyki ze znakiem 16-bitowym?)

Jednak przy odrobinie kłucia operacje na niepodpisanych wartościach do 32 bitów nie są zbyt złe, a większość ludzi nie potrzebuje niepodpisanego 64-bitowego podziału lub porównania.

Neil Coffey
źródło
2
Chciałbym też mieć bajty niepodpisane, ale podejrzewam, że korzyść wynikająca z pełnej spójności między typami liczb całkowitych przewyższa wygodę, jaką przyniosłyby bajty bez znaku.
Alan Moore,
64
„W codziennych celach czuli, że najczęstszą potrzebą były podpisane typy danych”. W moim kodzie C ++ częściej niż często myślę: „Dlaczego, u licha, używam tutaj liczby całkowitej ze znakiem zamiast liczby bez znaku ?!”. Mam wrażenie, że „podpisany” jest raczej wyjątkiem niż regułą (oczywiście zależy to od dziedziny, ale istnieje powód, dla którego dodatnie liczby całkowite nazywane są liczbami naturalnymi ;-)).
Luc Touraille,
15
kciuk w górę za wywołanie niepodpisanych bajtów, kiedy przetwarzam obraz, zakładając, że bajty są niepodpisane (tak jak powinny), zmusiłem mnie do debugowania godzin.
Helin Wang
7
zdziwiłbyś się, jak często shortjest używany - algorytmy defltate / gzip / inflate są 16-bitowe i polegają w dużej mierze na szortach ... lub przynajmniej short[][ co prawda są natywne - jednak algorytm Java przenoszący terrabytes danych]. Ta ostatnia ( short[]) ma znaczącą zaletę, int[]ponieważ zajmuje dwa razy mniej pamięci i mniej pamięci = lepsze właściwości buforowania, znacznie lepszą wydajność.
bestsss
8
Chociaż w konkretnej aplikacji powinieneś zmierzyć, czy użycie szortów daje lepszą wydajność, niż zakładając, że jest to prawdą. Możliwe jest, że dodatkowy jiggery-pokery wymagany do manipulowania skrótami zamiast ints (który jest zwykle typem, który procesor „lubi używać”) może w rzeczywistości mieć negatywny wpływ na wydajność w konkretnej aplikacji. Nie zawsze, ale powinieneś testować, a nie zakładać.
Neil Coffey,
19

To jest starsze pytanie, a Pat krótko wspomniał o char, pomyślałem, że powinienem rozwinąć tę kwestię dla innych, którzy popatrzą na to w dalszej części. Przyjrzyjmy się bliżej pierwotnym typom Java:

byte - 8-bitowa liczba całkowita ze znakiem

short - 16-bitowa liczba całkowita ze znakiem

int - 32-bitowa liczba całkowita ze znakiem

long - 64-bitowa liczba całkowita ze znakiem

char - 16-bitowy znak (liczba całkowita bez znaku)

Chociaż charnie obsługuje unsignedarytmetyki, zasadniczo można ją traktować jako unsignedliczbę całkowitą. Będziesz musiał jawnie rzucić operacje arytmetyczne z powrotem na char, ale zapewnia to sposób na określenie unsignedliczb.

char a = 0;
char b = 6;
a += 1;
a = (char) (a * b);
a = (char) (a + b);
a = (char) (a - 16);
b = (char) (b % 3);
b = (char) (b / a);
//a = -1; // Generates complier error, must be cast to char
System.out.println(a); // Prints ? 
System.out.println((int) a); // Prints 65532
System.out.println((short) a); // Prints -4
short c = -4;
System.out.println((int) c); // Prints -4, notice the difference with char
a *= 2;
a -= 6;
a /= 3;
a %= 7;
a++;
a--;

Tak, nie ma bezpośredniego wsparcia dla liczb całkowitych bez znaku (oczywiście, nie musiałbym przerzucać większości moich operacji z powrotem na char, gdyby było bezpośrednie wsparcie). Z pewnością istnieje jednak niepodpisany prymitywny typ danych. Chciałbym również zobaczyć bajt bez znaku, ale zdaje się, że podwojenie kosztu pamięci i zamiast tego użycie char jest realną opcją.


Edytować

Z JDK8 są nowe API dla Longi Integerktóre zapewniają metody pomocnika podczas leczenia longi intwartości jako wartości bez znaku.

  • compareUnsigned
  • divideUnsigned
  • parseUnsignedInt
  • parseUnsignedLong
  • remainderUnsigned
  • toUnsignedLong
  • toUnsignedString

Dodatkowo, Guava oferuje szereg metod pomocniczych do robienia podobnych rzeczy dla typów całkowitych, co pomaga wypełnić lukę pozostawioną przez brak natywnego wsparcia dla unsignedliczb całkowitych.

Jyro117
źródło
2
Jest jednak charzbyt mały, aby longna przykład obsługiwać arytmetykę.
3
Może to być wadą Java
Mając nadzieję, że obsługują nie podpisane wartości bajtów. Ułatwia wszystko.
mixz
15

Java ma typy niepodpisane lub przynajmniej jeden: char jest skrótem bez znaku. Więc bez względu na to, jaką wymówkę Gosling wyrzuca, tak naprawdę to jego ignorancja, dlaczego nie ma innych niepodpisanych typów.

Również typy krótkie: spodenki są używane przez cały czas do multimediów. Powodem jest to, że możesz zmieścić 2 próbki w jednym 32-bitowym długim znaku bez znaku i wektoryzować wiele operacji. To samo dotyczy danych 8-bitowych i bajtu bez znaku. W rejestrze można zmieścić 4 lub 8 próbek do wektoryzacji.

poklepać
źródło
37
Tak, jestem pewien, że Gosling jest bardzo nieświadomy języka Java w porównaniu z tobą.
jakeboxer
Czy Java pozwala na wykonywanie arytmetyki bezpośrednio na liczbach bez znaku, czy też zawsze wartości są promowane? Posiadanie niepodpisanego typu do przechowywania, ale zawsze wykonywanie arytmetyki na typie podpisanym, który jest wystarczająco duży, aby go pomieścić, działa dobrze semantycznie, ale spowodowałoby, że operacje na niepodpisanych typach, które byłyby tego samego rozmiaru co „normalne” liczby całkowite, byłyby droższe.
supercat
2
To zły styl do używania tylko chardla postaci.
starblue
5
@starblue Oczywiście, że tak, ale to hack, aby obejść ograniczenie języka
Basic
14

Jak tylko podpisane i niepodpisane ints są mieszane w wyrażeniu rzeczy zaczynają się bałagan i prawdopodobnie będzie tracić informacji. Ograniczenie Javy do podpisanych ints tylko naprawia wszystko. Cieszę się, że nie muszę się martwić całym biznesem podpisanym / niepodpisanym, chociaż czasami brakuje mi ósmego kawałka bajtu.

Bombe
źródło
12
Co do miksowania ze znakiem / niepodpisania: możesz mieć typy bez znaku, ale nie zezwalaj na miksowanie (lub wymagać jawnych rzutowań). Nadal nie jest jasne, czy jest to konieczne.
sleske,
2
W C ++ musisz dużo posypać, static_castaby je pomieszać. To jest naprawdę niechlujne.
Raedwald
4
8 bit jest tam, po prostu próbuje się ukryć jako znak.
starblue
Sprawa robi się bałagan tylko z typem 32 bitów lub większym. Nie widzę powodu, dla którego Java nie powinna była bytezostać podpisana tak jak w Pascal.
supercat
12
Przyjdź do mnie, gdy masz problemy z przetwarzaniem obrazu w Javie, gdzie oczekujesz, że bajty będą niepodpisane. Wtedy będziesz wiedział, że & 0xFFkażda promocja bajt-do-int sprawia, że ​​kod jest jeszcze bardziej bałaganiarski.
bit2shift,
12

http://skeletoncoder.blogspot.com/2006/09/java-tutorials-why-no-unsigned.html

Ten facet mówi, ponieważ standard C definiuje operacje z udziałem znaków niepodpisanych i podpisanych, które mają być traktowane jako niepodpisane. Może to spowodować, że liczby całkowite ze znakiem ujemnym zostaną przetoczone na dużą liczbę całkowitą bez znaku, potencjalnie powodując błędy.

akatakritos
źródło
34
Przetaczane są również liczby całkowite ze znakiem Java. Nie rozumiem twojego sensu.
foo
8
@foo: Podpisane liczby całkowite muszą stać się duże, zanim spowodują problemy. Natomiast w C można mieć problemy z porównaniem dowolnej ujemnej liczby całkowitej - nawet -1- z dowolną niepodpisaną wielkością - nawet zerem.
supercat
Szkoda, że ​​Java nie mogła zawierać niepodpisanych typów, ale z ograniczonym zestawem konwersji i operatorami mieszanymi (nieco analogicznie do tego, że w C można dodać 5 do wskaźnika, ale nie można porównać wskaźnika do 5) . Idea, że ​​użycie operatora na typach mieszanych, gdy istnieje niejawna rzutowanie, powinna wymusić niejawne użycie tego rzutowania (i użycie wynikowego typu jako typu wynikowego) leży u podstaw wielu podejrzanych decyzji projektowych zarówno w .NET, jak i Jawa.
supercat
4
Nie polegać na odpowiedzi, ale -1fakt, że wiek jest „nieznany” (jak sugeruje artykuł), jest jednym z klasycznych przykładów „zapachu kodu” . Na przykład, jeśli chcesz obliczyć „ile Alice jest starsza od Boba?”, A A = 25 i B = -1, otrzymasz odpowiedź, ±26która jest po prostu błędna. Prawidłowe obchodzenie się z nieznanymi wartościami jest jakaś Option<TArg>kiedy Some(25) - Nonewróci None.
bytebuster
11

Myślę, że Java jest w porządku, ale dodanie niepodpisanego skomplikowałoby go bez większego zysku. Nawet przy uproszczonym modelu liczb całkowitych większość programistów Java nie wie, jak zachowują się podstawowe typy liczbowe - wystarczy przeczytać książkę Java Puzzlers, aby zobaczyć, jakie błędne wyobrażenia możesz mieć.

Jeśli chodzi o praktyczne porady:

  • Jeśli twoje wartości są nieco dowolne i nie pasują int, użyj long. Jeśli nie pasują do longużycia BigInteger.

  • Używaj mniejszych typów tylko dla tablic, gdy potrzebujesz zaoszczędzić miejsce.

  • Jeśli potrzebujesz dokładnie 64/32/16/8 bitów, użyj long/ int/ short/ bytei przestań się martwić bitem znaku, z wyjątkiem podziału, porównania, przesunięcia w prawo i rzutowania.

Zobacz także odpowiedź na temat „przenoszenia generatora liczb losowych z C na Javę”.

starblue
źródło
5
Tak, aby przesunąć w prawo, musisz wybrać pomiędzy >>i odpowiednio >>>dla podpisanego i niepodpisanego. Przesunięcie w lewo nie stanowi problemu.
starblue,
1
@starblue W rzeczywistości >>>nie działa dla shorti byte. Na przykład (byte)0xff>>>1plony 0x7fffffffzamiast 0x7f. Kolejny przykład: byte b=(byte)0xff; b>>>=1;spowoduje b==(byte)0xff. Oczywiście możesz to zrobić, b=(byte)(b & 0xff >> 1);ale dodaje to jeszcze jedną operację (bitową i).
CITBL
7
„... Nawet w przypadku uproszczonego modelu większość programistów Java nie wie, jak zachowują się podstawowe typy numeryczne ...” Coś we mnie po prostu nie podoba się językowi o najniższym wspólnym mianowniku.
Podstawowy
Linia otwarcie w swojej odpowiedzi, o większej komplikacji i mały zysk, to właśnie ja opracowane w moim artykule 6 lat później: nayuki.io/page/unsigned-int-considered-harmful-for-java
Nayuki
1
@Nayuki Twój artykuł jest naprawdę fajny. Tylko mała uwaga, użyłbym dodania 0x80000000 dla operatorów porównania zamiast XOR, ponieważ wyjaśnia, dlaczego to działa, przesuwa ciągły region, w którym występuje porównanie z -MAXINT na 0. Bitowy efekt jest dokładnie taki sam.
starblue
6

Z JDK8 ma pewne wsparcie dla nich.

Mimo obaw Goslinga możemy jeszcze zobaczyć pełną obsługę niepodpisanych typów w Javie.

John Hascall
źródło
12
aka „Więc ludzie naprawdę tego używają i myliliśmy się, nie włączając go na początek” - ale wciąż nie ufamy programistom Java, że ​​wiedzą, czy zmienna jest podpisana, czy nie - więc nie zamierzamy ich implementować na maszynie wirtualnej lub jako typy równoważne z ich podpisanymi kuzynami.
Podstawowy
6

Wiem, że ten post jest za stary; jednak za zainteresowanie, w Java 8 i później, można użyć inttypu danych do reprezentowania niepodpisane 32-bitową liczbę całkowitą, która ma minimalną wartość 0 i wartość maksymalną w wysokości 2 32 -1. Użyj Integerklasy, aby użyć inttypu danych jako liczby całkowitej bez znaku, a do klasy dodano metody statyczne, takie jak itp. compareUnsigned(), W celu obsługi operacji arytmetycznych dla liczb całkowitych bez znaku.divideUnsigned()Integer

Morteza Adi
źródło
4

Słyszałem historie, że miały one zostać zawarte w pobliżu oryginalnego wydania Java. Dąb był prekursorem Javy, aw niektórych dokumentach specyfikacji wspomniano o użytych wartościach. Niestety nigdy nie trafiły one do języka Java. O ile ktokolwiek był w stanie się zorientować, po prostu nie został wdrożony, prawdopodobnie z powodu ograniczenia czasowego.

Rob Ottaway
źródło
To by było w porządku ... z wyjątkiem dowodów z wywiadu Goslinga sugeruje, że pominięto liczby całkowite (oprócz char), ponieważ projektanci uważali, że to zły pomysł ... biorąc pod uwagę cele języka.
Stephen C
Dobrym pomysłem jest, aby nigdy nie przykładać zbyt dużej wagi do zeznań naocznych świadków, jeśli dostępne są również dowody z dokumentów.
user7610,
4

Raz uczestniczyłem w kursie C ++ z kimś z komitetu normalizacyjnego C ++, który sugerował, że Java podjęła właściwą decyzję, aby unikać nieposiadających liczb całkowitych, ponieważ (1) większość programów używających liczb całkowitych bez znaku może równie dobrze robić z liczbami całkowitymi ze znakiem, a jest to bardziej naturalne w przypadku warunki myślenia ludzi i (2) używanie liczb całkowitych bez znaku powoduje wiele łatwych do utworzenia, ale trudnych do debugowania problemów, takich jak przepełnienie arytmetyczne liczb całkowitych i utrata znaczących bitów podczas konwersji między typami podpisanymi i niepodpisanymi. Jeśli przez pomyłkę odejmiesz 1 od 0 za pomocą liczb całkowitych ze znakiem, często szybciej powoduje awarię programu i łatwiej jest znaleźć błąd niż zawija się do 2 ^ 32 - 1, a kompilatory i narzędzia analizy statycznej i kontrole środowiska wykonawczego muszą Załóżmy, że wiesz, co robisz, ponieważ wybrałeś arytmetykę bez znaku. Również,

Dawno temu, kiedy pamięć była ograniczona, a procesory nie działały automatycznie na 64 bitach jednocześnie, każdy bit liczył się o wiele więcej, więc podpisywanie vs niepodpisane bajty lub skróty faktycznie miały znacznie większe znaczenie i była oczywiście właściwą decyzją projektową. Dzisiaj samo użycie int podpisanego jest więcej niż wystarczające w prawie wszystkich zwykłych przypadkach programowania, a jeśli twój program naprawdę musi używać wartości większych niż 2 ^ 31 - 1, często po prostu chcesz długo. Gdy znajdziesz się na terytorium używania długich, jeszcze trudniej jest wymyślić powód, dla którego tak naprawdę nie możesz sobie poradzić z dodatnimi liczbami całkowitymi 2 ^ 63 - 1. Ilekroć przejdziemy do 128-bitowych procesorów, będzie to jeszcze mniejszy problem.

Jonathan
źródło
2

Twoje pytanie brzmi „Dlaczego Java nie obsługuje niepodpisanych znaków int”?

A moja odpowiedź na twoje pytanie brzmi: Java chce, aby wszystkie jej prymitywne typy: bajt , char , short , int i long były traktowane odpowiednio jako bajt , słowo , dword i qword , dokładnie tak jak w asemblerze, a operatorzy Java są podpisani operacje na wszystkich typach pierwotnych oprócz char , ale tylko na char są 16-bitowe bez znaku.

Zatem metody statyczne mają być również operacjami niepodpisanymi dla 32-bitowego i 64-bitowego.

Potrzebujesz klasy końcowej, której metody statyczne można wywołać dla niepodpisanego operacji .

Możesz stworzyć tę ostatnią klasę, nazwać ją dowolną nazwą i zaimplementować jej metody statyczne.

Jeśli nie masz pojęcia, jak zaimplementować metody statyczne, kliknij ten link może ci pomóc.

Moim zdaniem, Java jest nie podobny do C ++ w ogóle , gdyby nie wsparcie niepodpisanych typów ani przeciążanie operatorów, więc myślę, że Java powinny być traktowane jako całkowicie inny język zarówno z C ++ i od C.

Nawiasem mówiąc, jest to zupełnie inna nazwa języków.

Dlatego nie polecam w Javie wpisywania kodu podobnego do C i wcale nie polecam wpisywania kodu podobnego do C ++, ponieważ wtedy w Javie nie będziesz mógł robić tego, co chcesz zrobić w C ++, tzn. kod w dalszym ciągu nie będzie w ogóle C ++, a dla mnie źle jest tak kodować, aby zmienić styl w środku.

Zalecam pisanie i używanie metod statycznych również dla operacji podpisanych, więc nie widzisz w kodzie mieszanki operatorów i metod statycznych zarówno dla operacji podpisanych, jak i niepodpisanych, chyba że potrzebujesz tylko podpisanych operacji w kodzie, i jest w porządku używaj tylko operatorów.

Również polecam aby uniknąć stosując krótkie , int i długo prymitywnych typów i użyć słowa , dword i qword odpowiednio zamiast, a ty o wywołać metody statyczne dla operacji bez znaku i / lub podpisanych operacji zamiast używać operatorów.

Jeśli masz zamiar zrobić tylko podpisane operacji i korzystania z operatorów tylko w kodzie, to jest w porządku, aby korzystać z tych prymitywnych typów short , int i długi .

Właściwie słowo , DWORD i qword zrobić nie istnieje w języku, ale można utworzyć nową klasę dla każdego i realizacja każdego powinno być bardzo proste:

Słowo klasowe zawiera tylko typ pierwotny krótki , dword klasowy posiada typ pierwotny int tylko, a słowo klasy zawiera tylko typ pierwotny długi . Teraz wszystkie metody niepodpisane i podpisane jako statyczne lub nie do wyboru, możesz zaimplementować w każdej klasie, tj. Wszystkie 16-bitowe operacje zarówno niepodpisane, jak i podpisane przez podanie nazw znaczeń w klasie słów , wszystkie 32-bitowe operacje zarówno niepodpisane, jak i podpisane przez podanie nazw znaczeń w klasie dword i wszystkie operacje 64-bitowe zarówno niepodpisane, jak i podpisane przez podanie nazw znaczeń w klasie qword .

Jeśli nie lubisz nadawać zbyt wielu różnych nazw dla każdej metody, zawsze możesz użyć przeciążenia w Javie, dobrze jest przeczytać, że Java też tego nie usunęła!

Jeśli chcesz metod zamiast operatorów dla 8-bitowych operacji podpisanych i metod dla 8-bitowych operacji niepodpisanych, które w ogóle nie mają operatorów, możesz utworzyć klasę Byte (zwróć uwagę, że pierwsza litera „B” jest wielka, więc nie jest to bajt typu pierwotnego ) i zaimplementuj metody w tej klasie.

O przekazywaniu według wartości i przekazywaniu przez referencję:

Jeśli się nie mylę, jak w języku C #, obiekty prymitywne są przekazywane wartościowo naturalnie, ale obiekty klasy są przekazywane referencyjnie naturalnie, co oznacza, że ​​obiekty typu Byte , word , dword i qword będą przekazywane przez referencję, a nie przez wartość domyślnie. Chciałbym, żeby Java miała obiekty struct tak jak C #, aby wszystkie Byte , word , dword i qword mogły być zaimplementowane w strukturze zamiast klasy, więc domyślnie były przekazywane przez wartość, a nie przez referencję domyślnie, jak każdy obiekt struct w C #, podobnie jak typy pierwotne, są przekazywane przez wartość, a nie przez referencję domyślnie, ale ponieważ Java jest gorsza niż C # i mamy aby sobie z tym poradzić, są tylko klasy i interfejsy, które są przekazywane przez referencję, a nie domyślnie przez wartość. Więc jeśli chcesz przekazać bajt , słowo , dword i qword obiektów przez wartość, a nie przez odniesienie, jak każdego innego obiektu klasy w Javie, a także w języku C #, trzeba będzie po prostu użyć konstruktora kopii i to wszystko.

To jedyne rozwiązanie, o którym mogę myśleć. Chciałbym tylko móc wpisać podstawowe typy słów, dwordów i qwordów, ale Java nie obsługuje ani typedef, ani w ogóle nie używa, w przeciwieństwie do C #, który obsługuje używanie , co jest równoważne typedef C.

O produkcji:

Dla tej samej sekwencji bitów możesz wydrukować je na wiele sposobów: jako binarne, jako dziesiętne (jak znaczenie% u w C printf), jako ósemkowe (jak znaczenie% o w C printf), jako szesnastkowe (jak znaczenie% x w C printf) i jako liczba całkowita (jak znaczenie% d w C printf).

Zauważ, że C printf nie zna typu zmiennych przekazywanych jako parametry do funkcji, więc printf zna typ każdej zmiennej tylko z obiektu char * przekazanego do pierwszego parametru funkcji.

Tak więc w każdej z klas: Bajt , słowo , dword i qword możesz zaimplementować metodę print i uzyskać funkcjonalność printf, mimo że pierwotny typ klasy jest podpisany, nadal możesz wydrukować go jako niepodpisany, postępując zgodnie z algorytmem obejmującym operacje logiczne i zmiany, aby uzyskać cyfry do wydrukowania na wyjściu.

Niestety podany przeze mnie link nie pokazuje, jak zaimplementować te metody drukowania, ale jestem pewien, że możesz znaleźć w Google algorytmy potrzebne do wdrożenia tych metod drukowania.

To wszystko, co mogę odpowiedzieć na twoje pytanie i zasugerować.


źródło
MASM (asembler Microsoft) i Windows definiują BYTE, WORD, DWORD, QWORD jako typy bez znaku. Dla MASM, SBYTE, SWORD, SDWORD, SQWORD są podpisane typy.
rcgldr
1

Ponieważ unsignedtyp jest czystym złem.

Fakt, że w C unsigned - intprodukuje, unsignedjest jeszcze bardziej zły.

Oto migawka problemu, który spalił mnie więcej niż raz:

// We have odd positive number of rays, 
// consecutive ones at angle delta from each other.
assert( rays.size() > 0 && rays.size() % 2 == 1 );

// Get a set of ray at delta angle between them.
for( size_t n = 0; n < rays.size(); ++n )
{
    // Compute the angle between nth ray and the middle one.
    // The index of the middle one is (rays.size() - 1) / 2,
    // the rays are evenly spaced at angle delta, therefore
    // the magnitude of the angle between nth ray and the 
    // middle one is: 
    double angle = delta * fabs( n - (rays.size() - 1) / 2 ); 

    // Do something else ...
}

Czy zauważyłeś już błąd? Przyznaję, że widziałem to dopiero po wejściu do debuggera.

Ponieważ njest typu bez znaku, size_tcałe wyrażenie n - (rays.size() - 1) / 2ocenia się jako unsigned. Że wyrażenie ma być podpisany pozycja nXX ray z jednym środkowym: 1st ray z jednym środkowym po lewej stronie musiałby pozycji -1, 1st jeden po prawej musiałby pozycji +1 itd Po biorąc wartość abs i mnożąc przez deltakąt, uzyskałbym kąt między npromieniem th a środkowym.

Niestety dla mnie powyższe wyrażenie zawierało zło niepodpisane i zamiast oceny, powiedzmy, -1, wyliczyło 2 ^ 32-1. Późniejsza konwersja w celu doublezapieczętowania błędu.

Po jednym lub dwóch błędach spowodowanych niewłaściwym użyciem unsignedarytmetyki należy zacząć zastanawiać się, czy dodatkowy bit, który dostajemy, jest wart dodatkowych kłopotów. Staram się, w miarę możliwości, unikać wszelkiego unsignedrodzaju typów w arytmetyce, chociaż nadal używam go do operacji nie arytmetycznych, takich jak maski binarne.

Michał
źródło
Dodanie „długiego niepodpisanego” do Javy byłoby niezręczne. Dodanie mniejszych typów bez znaku nie powinno jednak stanowić problemu. Szczególnie typy mniejsze niż „int” można było łatwo obsłużyć, promując je do „int” w liczbowo oczywisty sposób, a „unsigned int” można było obsłużyć, mówiąc, że operacje obejmujące podpisaną int i niepodpisaną int będą promować oba operandy na „długie”. Jedyną problematyczną sytuacją byłyby operacje obejmujące niepodpisaną długą i podpisaną ilość, ponieważ nie byłoby żadnego typu reprezentującego wszystkie wartości obu argumentów.
supercat
@ superupat: jeśli unsignedzostanie przekonwertowany intna każdą operację, z czego korzysta unsigned? Nie będzie miał żadnej funkcji, którą można by odróżnić short. A jeśli konwertujesz inttylko na operacje mieszane, takie jak unsigned+intlub unsigned+float, to nadal masz problem ((unsigned)25-(unsigned)30)*1.0 > 0, który jest główną przyczyną unsignedpowiązanych błędów.
Michael
Wiele operacji na niepodpisanych typach promuje się jako „długie”. Wymaganie jawnego rzutowania podczas przechowywania wyniku z powrotem do typów niepodpisanych spowodowałoby takie same niedogodności, jakie występują w przypadku krótkich i bajtów, ale jeśli typ jest głównie formatem pamięci, a nie formatem obliczeniowym, nie powinno to stanowić problemu. W każdym razie typy bez znaku krótsze niż „int” powinny po prostu móc bez problemu promować się na „int”.
supercat
3
Nie podoba mi się ta odpowiedź, ponieważ wykorzystuje ona argument „liczby całkowite bez znaku są złe i nie powinny istnieć, ponieważ nigdy nie można ich podpisać”. Każdy, kto próbuje odjąć liczbę całkowitą bez znaku, powinien już to wiedzieć. Jeśli chodzi o czytelność, C nie jest dokładnie znany z tego, że jest łatwy do naśladowania. Co więcej, (pół-) argument „dodatkowy bit nie jest wart dodatkowych kłopotów” jest również bardzo słaby. Czy obsługa błędów zamiast exit(1);naprawdę „jest warta dodatkowego problemu”? Czy nie jest w stanie otwierać dużych plików naprawdę wartych bezpieczeństwa, których nie doświadczą mniej doświadczeni programiści Java unsigned?
yyny
2
Jedyną złą rzeczą, którą widzę w tym kodzie, jest n - (rays.size() - 1) / 2. Zawsze powinieneś nawiasować operatory binarne, ponieważ czytnik kodu nie powinien zakładać niczego o kolejności operacji w programie komputerowym. To, że tradycyjnie mówimy, że a + b c = a + (b c) nie oznacza, że ​​możesz to założyć podczas czytania kodu. Ponadto obliczenia należy zdefiniować poza pętlą, aby można je było przetestować bez pętli. Jest to błąd polegający na tym, że nie wyrównujesz typów, a nie na liczbach całkowitych bez znaku. W C musisz upewnić się, że Twoje typy są w linii.
Dmitry
0

W specyfikacji „C” jest kilka klejnotów, które Java upuściła z powodów pragmatycznych, ale które powoli cofają się wraz z popytem programistów (zamknięcia itp.).

Wspominam o pierwszym, ponieważ jest on związany z tą dyskusją; zgodność wartości wskaźnika z arytmetyką całkowitą bez znaku. I w związku z tym tematem wątku trudność w utrzymywaniu semantyki Unsigned w podpisanym świecie Java.

Sądzę, że gdyby ktoś otrzymał alter ego Dennisa Ritchiego, który doradziłby zespołowi projektowemu Goslinga, zasugerowałby nadanie Signedowi „zero w nieskończoności”, tak aby wszystkie żądania przesunięcia adresu najpierw dodawałyby ROZMIAR PIERŚCIENIA ALGEBRAICZNEGO, aby uniknąć ujemnych wartości.

W ten sposób każde przesunięcie rzucone na tablicę nigdy nie wygeneruje SEGFAULT. Na przykład w klasie enkapsulowanej, którą nazywam RingArray podwójnych, która wymaga zachowania bez znaku - w kontekście „samoobrotowej pętli”:

// ...
// Housekeeping state variable
long entrycount;     // A sequence number
int cycle;           // Number of loops cycled
int size;            // Active size of the array because size<modulus during cycle 0
int modulus;         // Maximal size of the array

// Ring state variables
private int head;   // The 'head' of the Ring
private int tail;   // The ring iterator 'cursor'
// tail may get the current cursor position
// and head gets the old tail value
// there are other semantic variations possible

// The Array state variable
double [] darray;    // The array of doubles

// somewhere in constructor
public RingArray(int modulus) {
    super();
    this.modulus = modulus;
    tail =  head =  cycle = 0;
    darray = new double[modulus];
// ...
}
// ...
double getElementAt(int offset){
    return darray[(tail+modulus+offset%modulus)%modulus];
}
//  remember, the above is treating steady-state where size==modulus
// ...

Powyższy RingArray nigdy nie „pobierałby” z indeksu ujemnego, nawet gdyby próbował go złośliwy requester. Pamiętaj, że istnieje również wiele uzasadnionych próśb o podanie wcześniejszych (ujemnych) wartości indeksu.

NB: Zewnętrzny moduł% usuwa odniesienia do uzasadnionych żądań, podczas gdy wewnętrzny moduł% maskuje jawną złośliwość przed negatywami bardziej negatywnymi niż -modulus. Gdyby miało się to kiedykolwiek pojawić w Javie + .. + 9 || 8 + .. + spec, problem rzeczywiście stałby się „programistą, który nie może„ samobrócić się ”USTERKA.

Jestem pewien, że tak zwany „brak” języka Java unsigned int można uzupełnić powyższym linkiem.

PS: Aby nadać kontekst powyższemu porządkowi RingArray, oto kandydująca operacja „ustaw”, aby dopasować do powyższej operacji elementu „pobierz”:

void addElement(long entrycount,double value){ // to be called only by the keeper of entrycount
    this.entrycount= entrycount;
    cycle = (int)entrycount/modulus;
    if(cycle==0){                       // start-up is when the ring is being populated the first time around
        size = (int)entrycount;         // during start-up, size is less than modulus so use modulo size arithmetic
        tail = (int)entrycount%size;    //  during start-up
    }
    else {
        size = modulus;
        head = tail;
        tail = (int)entrycount%modulus; //  after start-up
    }
    darray[head] = value;               //  always overwrite old tail
}
MKhomo
źródło
-2

Mogę wymyślić jeden niefortunny efekt uboczny. W osadzonych bazach danych Java liczba identyfikatorów, które można mieć przy 32-bitowym polu identyfikatora, wynosi 2 ^ 31, a nie 2 ^ 32 (~ 2 miliardy, nie ~ 4 miliardy).

mike g
źródło
1
Prawdopodobnie myśli o tablicach i nie jest w stanie używać ujemnych liczb całkowitych jako wskaźników. Prawdopodobnie.
SK9
2
Kiedy pola automatycznego przyrostu w bazach danych są przepełnione, często idą wacko.
Joshua
-8

Powodem IMHO jest to, że są / byli zbyt leniwi, aby zaimplementować / poprawić ten błąd. Sugerowanie, że programiści C / C ++ nie rozumieją niepodpisanej, struktury, unii, flagi bitowej ... Jest po prostu niedorzeczne.

Eter rozmawiałeś z podstawowym programistą / bash / java na progu programowania a la C, bez żadnej realnej znajomości tego języka lub po prostu rozmawiasz ze swojego umysłu. ;)

kiedy codziennie rozprawiasz się z formatem albo z pliku, albo ze sprzętu, zaczynasz pytać, co do diabła myślą.

Dobrym przykładem może być tutaj próba użycia niepodpisanego bajtu jako samobrotującej się pętli. Dla tych z was, którzy nie rozumieją ostatniego zdania, jak do diabła nazywacie siebie programistą.

DC

Denis Co
źródło
34
Tylko dla kopnięć, Google wyrażenie „samoobrotowa pętla”. Najwyraźniej Denis Co jest jedyną osobą na świecie, która nazywa się programistą :-)
Stephen C
6
Ta odpowiedź jest tak zła, że ​​zabawna
Nayuki,