Jak już zapewne czytałeś/-aś, od 1 lipca jestem pracownikiem Citi w Warszawie na pozycji lidera zespołu ds rozwoju aplikacji.
Tytuł szumny, a na chwilę obecną sprowadza się do budowania zespołu - rozmowy kwalifikacyjne, monitorowanie procesu rekrutacyjnego od strony systemów HR oraz kontaktu z przyszłym pracownikiem i inne równie nietechniczne sprawy. Ktoś mógłby mniemać, że nuda, ale mimo, że nietechnicznie, jestem zachwycony móc to robić. Zawsze to coś innego, a wcale nie mniej zachwycającego.
Jednak sprawy techniczne czekają na mnie i do zespołu będzie rekrutowana osoba na stanowisko "chief of stuff", co zniesie ze mnie obsługę spraw mniej technicznych w zespole. Czujesz się na siłach, aby podjąć się trudów organizacji pracy zespołu? Skontaktuj się ze mną. Namiary na oficjalną ofertę pracy w Citi niebawem.
Ale co skłoniło mnie do opublikowania tego wpisu, to inne wakaty, które wciąż czekają na obsadzenie w moim zespole - Credit Front Office Framework Developer.
Stanowisko wymaga niebagatelnych umiejętności, wśród których znaleźć można znajomość algorytmiki, rozległa znajomość programowania w Javie oraz Scala, Clojure i Hadoop. Do tego dobra znajomość angielskiego w mowie i częste zwycięstwa w walce ze spiętrzeniami w projekcie. Oczywiście nie wszystko jednocześnie, a jedynie czym więcej, tym lepiej. Umiejętność logicznego rozkładania problemu i oprogramowania go w wybranym języku jest najbardziej pożądaną cechą. Więcej w oficjalnej ofercie pracy na to stanowisko (gdyby link nie prowadził do oferty, wystarczy wyszukać frazy "java scala" w obszarze EMEA/Polska/Mazowieckie/Warszawa). W razie pytań, proszę o kontakt ze mną - chętnie odpowiem na wszelkie pytania.
Do zespołu poszukiwani są mniej zaawansowani programiści (juniorzy) jak i wymiatacze. Na dzień dzisiejszy mamy w zespole 11 nietuzinkowych osób (pozdrowienia dla +Grzegorz Balcerek, +Paweł Cesar Sanjuan Szklarz, +Marcin Pieciukiewicz, +Artur Stanek), a potrzebujemy jeszcze raz tyle (!) Roboty jest cała masa różnej maści - od babrania się w błotku po bezkresy refaktorowania w Javie i Clojure oraz pomału i w Scali. Pomożesz? Koniecznie o tym porozmawiajmy!
W takim zespole mógłbym nawet robić za sprzątaczkę. Bezpośredni dostęp do (wiedzy) członków tego zespołu uważam za największy zysk mojego przejścia do Citi. A nadchodzą kolejni i to równie wyczesani!
Pokazywanie postów oznaczonych etykietą java. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą java. Pokaż wszystkie posty
22 sierpnia 2013
20 września 2012
Współbieżność i Android na Mobilization^2
W najbliższą sobotę będę miał przyjemność występować na konferencji Mobilization^2 w Łodzi, podczas której przedstawię temat "Zrównoleglanie zadań w Javie na platformie Android". Prezentacja będzie podsumowaniem dotychczasowych doświadczeń w rozpoznaniu tematu zrównoleglania zadań na platformie Android i liczę na kontakt z osobami, których tematyka intryguje, a moje wystąpienie stanie się swoistym katalizatorem dalszych studiów.
Prezentacja zaplanowana jest na godzinę 10:15 w sali F10 – Fortress. Reszta na stronie konferencji. Zapraszam!
Prezentacja zaplanowana jest na godzinę 10:15 w sali F10 – Fortress. Reszta na stronie konferencji. Zapraszam!
15 maja 2012
Po dwóch sesjach "Informatycznych Technologii Biznesowych" (ITB) na UAM w Poznaniu
Umożliwiono mi poprowadzenie pewnego projektu naukowego, którego celem jest zrealizowanie 24 godzin wykładów i ćwiczeń w ramach programu dwusemestralnych studiów podyplomowych "Informatyczne Technologie Biznesowe" (ITB) na Wydziale Matematyki i Informatyki Uniwersytetu im. Adama Mickiewicza.
W ten sposób mogę podzielić się swoją wiedzą w temacie programowania w Javie, Java EE 6 oraz produktów z rodziny IBM WebSphere, głównie IBM WebSphere Application Server V8 oraz V8.5 Libery Profile. Wybaczcie określenie, ale uczestnicy są pewnego rodzaju moim "materiałem poznawczym", który pozwala mi uzmysłowić sobie, jak rzeczy proste wcale takimi nie muszą być, a wszystko zależy od doświadczenia.
Kiedy ustalałem materiał do omówienia, planowałem wiele, sądząc, że 24 godziny to szmat czasu. I myliłby się ten, kto tak sądzi. Doświadczenie w wystąpieniach publicznych pomaga, ale tutaj zmienna czasu się znacząco wydłużyła, więc należało podzielić całość na małe prezentacje. Poza tym, jeśli zebrany materiał przygotowawczy nie pozwala na dopasowanie tematyki, to sprawa staje się karkołomna. Rozpoznanie audytorium jest kluczem do sukcesu sprawnego przekazu. Marzy mi się, aby po moich wystąpieniach znalazły się osoby, które pozwolą sobie na chwilę zastanowienia nad omówionym tematem. Marzy mi się, aby uczestnicy byli aktywni, wiedzieli, czego oczekują w zamian za poświęcony czas - swój czas. To jest inwestycja, którą wielu sprowadza do poziomu "odsiedzę swoje i będzie z bańki". Jeszcze mniej osób chce i potrafi wyrazić swoją opinię. Szkoda.
To sprawia, że praca z klientem (w tej konfiguracji są to słuchacze-studenci ITB) jest nieprzewidywalna i stąd okrywcza. A dodatkowo można wiele się nauczyć o materiale, który zdawało się, że rozumiem. Ot, choćby ostatnie próby z uruchomieniem executable jar, co w nomenklaturze Eclipse nazywa się Runnable Jar, a jest wykonywalnym plikiem jar z Main-Class w MANIFEST.MF.
Nie tylko nie wiedziałem o istnieniu asystenta Runnable JAR file w Eclipse, ale również później o powiązaniu między nim a konfiguracją uruchomieniową projektu.
Nie wiedziałem, bo nie było potrzebne, ale jak tu przeprowadzić wprowadzenie do Javy bez wyjaśnienia tego mechanizmu?! Teraz już wiem i kolejni będą mogli podziwiać moją wiedzę (a ja nie powiem, że jeszcze chwilę temu tego nie wiedziałem - niech sobie myślą, że się z tym urodziłem!).
I tak nieprzewidywalnie było jeszcze kilkakrotnie, kiedy kwitowałem pytania, uwagi, dyskusje stwierdzeniem "Wy to mi robicie specjalnie!" Innymi słowy, często padały pytania, które wcześniej zostały już wyjaśnione, które wymagały jedynie przeczytania, co napisane na ekranie, czy wręcz pomyślenia. Dzięki dwóm sesjom w ramach ITB nauczyłem się, że nie wszystko oczywiste, co jest dla mnie oczywiste, a wszystko wymaga czasu, skupienia i cierpliwości. Trzeba pozwolić na pytania i umiejętnie sugerować chwilę zastanowienia. Wciąż uczę się tej magicznej sztuki nauczania. Z trójką dzieci w domu wydaje się, że powinno być prościej, ale moje osiągnięcia na tym polu świadczą, że chyba jestem oporny na tę naukę.
Jeszcze przed pierwszą sesją ostrzegano mnie, żeby sobie nie urządzić wprowadzenia do programowania w Javie. Dla mnie poznawanie produktów IBM WebSphere przez osoby, które chcą wiedzieć więcej (rozumiem, że ITB, to właśnie wyrażenie tej potrzeby, że chce się coś więcej) sprowadza się do znajomości mechanizmów języka Java, w którym zostały napisane. Dlaczego? Wychodzę z założenia, że jeśli zna się podstawy, tj. składowe, to zrozumienie złożonej materii przychodzi łatwiej. Nie jest to warunek konieczny, ale przy odrobinie otwartości umysłu można poznać więcej, szybciej. Ja tego doświadczam i mam wrażenie, że inni również mogą.
Kiedy zapytałem uczestników, jakie są ich oczekiwania, zapadła cisza. Wyobraź sobie moją minę, kiedy próbuję dopasować materiał do uczestników, a oni sami nie wiedzą, czego chcą! Po sesji przedstawiania się oraz możliwego materiału edukacyjnego, odniosłem wrażenie, że wielu byłoby skłonnych poznać tajniki programowania w Javie.
I się zaczęło (w pozytywnym tego słowa znaczeniu!)
Zaczęliśmy od tworzenia projektów javowych w Eclipse. Kilka klas do uruchomienia z linii poleceń, później wspomniany wykonywalny jar, do tego aplikacja graficzna i na koniec servlet z IBM WebSphere Application Server 8.5 Liberty Profile.
Odnoszę wrażenie, że było to niesamowite doświadczenie dla obu stron. Przekrój naukowy, wiekowy oraz płciowy nie pozostawia złudzeń, że tak zróżnicowanej grupy nie miałem jeszcze wcześniej. I Java się podobała (co składam również na barki nieinwazyjnego sposobu, w jakim ją wprowadziłem - chwila pochwały dla siebie ku poprawieniu nastroju).
Na kolejnym spotkaniu mamy do dyspozycji 6 godzin i na moją propozycję, aby zabawić się z Androidem, grupa odpowiedziała gromkim "TAK!" Zaproponowałem, aby uczestnicy przynieśli swoje smartfony, bo nic tak nie daje kopa ku dalszemu rozwojowi, jak możliwość korzystania z tego, co samemu się stworzyło. Dawno to było, kiedy siedziałem przy Androidzie, a teraz będzie okazja do odświeżenia materiału. Już nie mogę doczekać się, kiedy zobaczę ich twarze, po tym, jak uruchomią pierwsze HelloWorld na swoich smartfonach. Oj, będzie się działo!
Na zakończenie wycinek z korespondencji, którą dostałem od jednego z uczestników:
"Chciałbym poznać środowisko Java od podstaw, ale ukierunkowując swoje zainteresowania w kierunku tworzenia dynamicznych stron WWW. Coś mi mówi, że w tym języku drzemie większy potencjał niż w PHP. Może i się mylę, ale jak nie sprawdzę to się nie dowiem. Tak czy siak skutecznie mnie zachęciłeś by choć spróbować."
I właśnie to było moim celem! Nie mam złudzeń, że 24 godziny to zdecydowanie za mało, aby pozwolić rozkoszować się pięknem języka Java (trzeba najpierw nauczyć się jej odpowiednio smakować), ale zaintrygować nią, to już wystarczy. Ten mail jest dopełnieniem mojego szczęścia. Kiedy dodam, że na sali znalazła się pani nauczycielka, która zechciała popróbować się samodzielnie z poznaniem Javy w ramach zadania domowego, mojego szczęścia jest więcej niż jedna osoba mogłaby skonsumować :-)
Kolejne "doświadczenie naukowe" już podczas tegotygodniowego GeeCON, podczas którego wystąpię z prezentacją "A whirlwind tour of Clojure", aby po niej pojawić się na spotkaniu Warszawa JUG 22 maja z tematem "Budowanie aplikacji Java EE 6 z Clojure" (zapraszam do dyskusji o jego formie i tematyce), aby pojawić się w Ustroniu 28 maja, Tarnowie 29-30 maja i Warszawie na konferencji Softdevcon 31 maja. Do zobaczenia!
p.s. Swoje doświadczenia zbieram w postaci materiałów szkoleniowych, które mam nadzieję opublikować niebawem. Pisz na priv, jeśli jesteś zainteresowany/a poznaniem szczegółów lub udziałem w projekcie jako beta tester. "Amicorum omnia communia" jak mówią.
W ten sposób mogę podzielić się swoją wiedzą w temacie programowania w Javie, Java EE 6 oraz produktów z rodziny IBM WebSphere, głównie IBM WebSphere Application Server V8 oraz V8.5 Libery Profile. Wybaczcie określenie, ale uczestnicy są pewnego rodzaju moim "materiałem poznawczym", który pozwala mi uzmysłowić sobie, jak rzeczy proste wcale takimi nie muszą być, a wszystko zależy od doświadczenia.
Kiedy ustalałem materiał do omówienia, planowałem wiele, sądząc, że 24 godziny to szmat czasu. I myliłby się ten, kto tak sądzi. Doświadczenie w wystąpieniach publicznych pomaga, ale tutaj zmienna czasu się znacząco wydłużyła, więc należało podzielić całość na małe prezentacje. Poza tym, jeśli zebrany materiał przygotowawczy nie pozwala na dopasowanie tematyki, to sprawa staje się karkołomna. Rozpoznanie audytorium jest kluczem do sukcesu sprawnego przekazu. Marzy mi się, aby po moich wystąpieniach znalazły się osoby, które pozwolą sobie na chwilę zastanowienia nad omówionym tematem. Marzy mi się, aby uczestnicy byli aktywni, wiedzieli, czego oczekują w zamian za poświęcony czas - swój czas. To jest inwestycja, którą wielu sprowadza do poziomu "odsiedzę swoje i będzie z bańki". Jeszcze mniej osób chce i potrafi wyrazić swoją opinię. Szkoda.
To sprawia, że praca z klientem (w tej konfiguracji są to słuchacze-studenci ITB) jest nieprzewidywalna i stąd okrywcza. A dodatkowo można wiele się nauczyć o materiale, który zdawało się, że rozumiem. Ot, choćby ostatnie próby z uruchomieniem executable jar, co w nomenklaturze Eclipse nazywa się Runnable Jar, a jest wykonywalnym plikiem jar z Main-Class w MANIFEST.MF.
Nie tylko nie wiedziałem o istnieniu asystenta Runnable JAR file w Eclipse, ale również później o powiązaniu między nim a konfiguracją uruchomieniową projektu.
Nie wiedziałem, bo nie było potrzebne, ale jak tu przeprowadzić wprowadzenie do Javy bez wyjaśnienia tego mechanizmu?! Teraz już wiem i kolejni będą mogli podziwiać moją wiedzę (a ja nie powiem, że jeszcze chwilę temu tego nie wiedziałem - niech sobie myślą, że się z tym urodziłem!).
I tak nieprzewidywalnie było jeszcze kilkakrotnie, kiedy kwitowałem pytania, uwagi, dyskusje stwierdzeniem "Wy to mi robicie specjalnie!" Innymi słowy, często padały pytania, które wcześniej zostały już wyjaśnione, które wymagały jedynie przeczytania, co napisane na ekranie, czy wręcz pomyślenia. Dzięki dwóm sesjom w ramach ITB nauczyłem się, że nie wszystko oczywiste, co jest dla mnie oczywiste, a wszystko wymaga czasu, skupienia i cierpliwości. Trzeba pozwolić na pytania i umiejętnie sugerować chwilę zastanowienia. Wciąż uczę się tej magicznej sztuki nauczania. Z trójką dzieci w domu wydaje się, że powinno być prościej, ale moje osiągnięcia na tym polu świadczą, że chyba jestem oporny na tę naukę.
Jeszcze przed pierwszą sesją ostrzegano mnie, żeby sobie nie urządzić wprowadzenia do programowania w Javie. Dla mnie poznawanie produktów IBM WebSphere przez osoby, które chcą wiedzieć więcej (rozumiem, że ITB, to właśnie wyrażenie tej potrzeby, że chce się coś więcej) sprowadza się do znajomości mechanizmów języka Java, w którym zostały napisane. Dlaczego? Wychodzę z założenia, że jeśli zna się podstawy, tj. składowe, to zrozumienie złożonej materii przychodzi łatwiej. Nie jest to warunek konieczny, ale przy odrobinie otwartości umysłu można poznać więcej, szybciej. Ja tego doświadczam i mam wrażenie, że inni również mogą.
Kiedy zapytałem uczestników, jakie są ich oczekiwania, zapadła cisza. Wyobraź sobie moją minę, kiedy próbuję dopasować materiał do uczestników, a oni sami nie wiedzą, czego chcą! Po sesji przedstawiania się oraz możliwego materiału edukacyjnego, odniosłem wrażenie, że wielu byłoby skłonnych poznać tajniki programowania w Javie.
I się zaczęło (w pozytywnym tego słowa znaczeniu!)
Zaczęliśmy od tworzenia projektów javowych w Eclipse. Kilka klas do uruchomienia z linii poleceń, później wspomniany wykonywalny jar, do tego aplikacja graficzna i na koniec servlet z IBM WebSphere Application Server 8.5 Liberty Profile.
Odnoszę wrażenie, że było to niesamowite doświadczenie dla obu stron. Przekrój naukowy, wiekowy oraz płciowy nie pozostawia złudzeń, że tak zróżnicowanej grupy nie miałem jeszcze wcześniej. I Java się podobała (co składam również na barki nieinwazyjnego sposobu, w jakim ją wprowadziłem - chwila pochwały dla siebie ku poprawieniu nastroju).
Na kolejnym spotkaniu mamy do dyspozycji 6 godzin i na moją propozycję, aby zabawić się z Androidem, grupa odpowiedziała gromkim "TAK!" Zaproponowałem, aby uczestnicy przynieśli swoje smartfony, bo nic tak nie daje kopa ku dalszemu rozwojowi, jak możliwość korzystania z tego, co samemu się stworzyło. Dawno to było, kiedy siedziałem przy Androidzie, a teraz będzie okazja do odświeżenia materiału. Już nie mogę doczekać się, kiedy zobaczę ich twarze, po tym, jak uruchomią pierwsze HelloWorld na swoich smartfonach. Oj, będzie się działo!
Na zakończenie wycinek z korespondencji, którą dostałem od jednego z uczestników:
"Chciałbym poznać środowisko Java od podstaw, ale ukierunkowując swoje zainteresowania w kierunku tworzenia dynamicznych stron WWW. Coś mi mówi, że w tym języku drzemie większy potencjał niż w PHP. Może i się mylę, ale jak nie sprawdzę to się nie dowiem. Tak czy siak skutecznie mnie zachęciłeś by choć spróbować."
I właśnie to było moim celem! Nie mam złudzeń, że 24 godziny to zdecydowanie za mało, aby pozwolić rozkoszować się pięknem języka Java (trzeba najpierw nauczyć się jej odpowiednio smakować), ale zaintrygować nią, to już wystarczy. Ten mail jest dopełnieniem mojego szczęścia. Kiedy dodam, że na sali znalazła się pani nauczycielka, która zechciała popróbować się samodzielnie z poznaniem Javy w ramach zadania domowego, mojego szczęścia jest więcej niż jedna osoba mogłaby skonsumować :-)
Kolejne "doświadczenie naukowe" już podczas tegotygodniowego GeeCON, podczas którego wystąpię z prezentacją "A whirlwind tour of Clojure", aby po niej pojawić się na spotkaniu Warszawa JUG 22 maja z tematem "Budowanie aplikacji Java EE 6 z Clojure" (zapraszam do dyskusji o jego formie i tematyce), aby pojawić się w Ustroniu 28 maja, Tarnowie 29-30 maja i Warszawie na konferencji Softdevcon 31 maja. Do zobaczenia!
p.s. Swoje doświadczenia zbieram w postaci materiałów szkoleniowych, które mam nadzieję opublikować niebawem. Pisz na priv, jeśli jesteś zainteresowany/a poznaniem szczegółów lub udziałem w projekcie jako beta tester. "Amicorum omnia communia" jak mówią.
03 stycznia 2012
O operatorach przesunięć w Javie...prawie wszystko
Na zakończenie 2011 zasiadłem do rozpoznania wewnętrznej reprezentacji liczb całkowitych w Javie i w ten sposób powstały dwa wpisy:
Dla przypomnienia:
& (AND) zwraca 1, wyłącznie jeśli oba bity są 1.
^ (XOR) zwraca 0, jeśli oba bity są jednocześnie 0 lub 1.
| (OR) zwraca 0, wyłącznie jeśli oba bity są 0.
Kolejność działań: &, ^, |
Jeśli typem lewego argumentu jest int, jedynie 5 najmłodszych bitów jest branych pod uwagę. Dlaczego? Taka jest długość bitowej reprezentacji int, tzn. 5 bitów pozwala na określenie zakresu przesunięcia od 0 do 31 włącznie, a tylko takie ma sens w przypadku 32 bitowej reprezentacji liczby całkowitej. W myśl tej zasady prawy argument jest zawsze pomniejszany przez zastosowanie operatora logicznego AND z maską 0x1F (0b11111), aby nie wyjść poza zakres [0, 31].
Jeśli jednak typem lewego argumentu jest long, wtedy jedynie 6 najmłodszych bitów jest branych pod uwagę. Ponownie, tylko tyle bitów - 2^6 - służy do reprezentacji bitowej dowolnej liczby long. Tym razem stosujemy logiczny AND z maską 0x3F (= 0b111111) i przesunięcie jest wykonane w zakresie 0 do 63 włącznie.
Lewy argument typu long nie wymusza promocji argumentu prawego do long, gdyż i tak int jest wystarczający, aby określić zakres przesunięcia [0-63] (przez zastosowanie maski 0x3F).
Przesunięcie w lewo ze znakiem jest równoważne iloczynowi lewego operandu przez 2 do potęgi s, tj. n * 2^s.
Przykład: 11 << 3 = 11 * 2^3 = 11 * 8 = 88
Dla liczb nieujemnych, przesunięcie w prawo ze znakiem jest równoważne z obliczeniem ilorazu (bez reszty) lewego operandu przez 2 do potęgi s, tj. n / 2^s.
Przykład: 11 >> 3 = 11 / 2^3 = 11 / 8 = 1
Z przesunięciem bez znaku "wypełniaczem" staje się 0, co może zamazać bit znaku i zmienić znak wyniku, który zawsze będzie dodatni.
Próbując wyrazić działanie >>> wzorem stosuje się następującą zasadę - jeśli lewy operand jest dodatni, wtedy wynik jest identyczny z prawym przesunięciem ze znakiem. Jeśli jednak lewy operand jest ujemy, wtedy wynik równa się (n >> s) + (2 << ~s). I właśnie ten wzór mnie zmroził najbardziej. Aż mnie do tej pory ciarki przechodzą :)
Przykład: 11 >>> 3 = 11 >> 3 = 11 / 2^3 = 1
Tylko skąd pomysł na >>>?! Na pewno nie zamierzam zapamiętać wzoru na wynik (chociaż przy przeglądaniu Sieci i spisywaniu wiedzy już zagnieździło się w głowie). Musi być tego jakieś sensowne uzasadnienie.
Dopiero w The Unsigned Right Shift olśniło mnie, kiedy trafiłem na możliwe zastosowanie operatora prawego przesunięcia bez znaku "shifting bits that does not represent a numeric value, e.g. pixel-based values and graphics."
I uzupełniając, w Bit twiddling in Java: The Unsigned Right Shift Operator:
Mimo, że każdy ciąg bitów o długości mniejszej niż 64 (najdłuższy typ w Javie - long) reprezentuje pewną liczbę całkowitą, to jedynie przesunięcia ze znakiem szanują reprezentację ciągu w postaci liczby - zachowują znak, a on ma znaczenie przy liczbach. W przypadku przesunięcia w prawo bez znaku >>> reprezentacja liczbowa nie ma znaczenia, a jedynie ciąg bitów - właśnie przez brak dbałości o znak liczby.
Innymi słowy, główne zastosowanie prawego przesunięcia bez znaku to sterowniki urządzeń, niskopoziomowa obsługa grafiki i formatów graficznych, pakiety komunikacyjne, kryptografia. W mojej karierze z Javą nigdy jednak nie spotkałem się z takimi zadaniami, ani chociażby minimalnego użycia operatorów przesunięcia. Zdają się być zbyt magiczne, aby miały zastosowanie nad chociażby java.lang.Math.pow(double, double). Mówi się również, że maksymalna liczba możliwych pytań z operatorów przesunięć na egzaminie z SCJP nie przekracza...jednego (!)
Zagadka: Jaki będzie wynik dowolnego przesunięcia liczby 0xFFFFFFFF (8 znaków F, tj. 32 jedynki) dla operatorów prawego przesunięcia ze znakiem?
W artykule Bitwise operation znalazłem bardzo intrygujące zachowanie przesunięć.
Zacznijmy od zagadki: Jaki będzie wynik odpowiednich przesunięć - (-12 >> 2) << 2, (-12 << 2) >> 2, (-12 >>> 2) << 2, (-12 << 2) >>> 2?
UWAGA: Przesunięcia typów mniej pojemnych, które będą rozszerzane do int - do 32 bitów, np. 8-bitowy byte rozszerzany jest do 32 bitowego int, więc przesunięcie >>> może nie mieć żadnego wpływu i należy maskować liczbę przed przesunięciem, np. (b & 0xFF) >>> 2.
Przesunięcie w prawo zawsze skutkuje "wypadaniem" liczb poza koniec (jakby wpadały w przepaść i były tracone na zawsze).
W The SCJP Tip Line Bit Shifting by Corey McGlone trafiłem na ciekawą zagadkę, która wynika bezpośrednio z powyższych reguł przesunięcia liczb całkowitych (szczególnie maskowaniem prawego operandu).
Zagadka: Jaki będzie wynik 3 >>> 32?
Na odpowiedź masz jedynie sekundę :)
Pamiętasz maskowanie prawego operandu przed wykonaniem przesunięcia? Dla int będzie to maska 0x1F, czyli 32. Czy teraz łatwiej odpowiedzieć na powyższe pytanie? To jaki będzie wynik?
Wystarczy zastosować mod 32 i masz gotową odpowiedź.
3 >>> 32 = 3 >>> (32 % 32) = 3 >>> 0 = 3
Fajne, co?! Właśnie takie cuda w Javie i w ogóle w językach programowania nakręcają mnie na dalsze ich zgłębianie. Czego i Tobie życzę w Nowym Roku 2012! :)
- Kod uzupełnień do dwóch w Javie (reprezentacja binarna liczb całkowitych)
- O kodzie uzupełnień do dwóch w Javie raz jeszcze
Dla przypomnienia:
& (AND) zwraca 1, wyłącznie jeśli oba bity są 1.
^ (XOR) zwraca 0, jeśli oba bity są jednocześnie 0 lub 1.
| (OR) zwraca 0, wyłącznie jeśli oba bity są 0.
Kolejność działań: &, ^, |
Wstępniak
Java definiuje 3 operatory przesunięcia - w lewo ze znakiem <<, w prawo ze znakiem >> i w prawo bez znaku >>>. Wszystkie działają wyłącznie na typach całkowitych long (64 bity) lub int (32 bity) i typ lewego argumentu wyznacza typ prawego. W przypadku węższych typów - short, byte, char - są one promowane do int i to po obu stronach niezależnie.Jeśli typem lewego argumentu jest int, jedynie 5 najmłodszych bitów jest branych pod uwagę. Dlaczego? Taka jest długość bitowej reprezentacji int, tzn. 5 bitów pozwala na określenie zakresu przesunięcia od 0 do 31 włącznie, a tylko takie ma sens w przypadku 32 bitowej reprezentacji liczby całkowitej. W myśl tej zasady prawy argument jest zawsze pomniejszany przez zastosowanie operatora logicznego AND z maską 0x1F (0b11111), aby nie wyjść poza zakres [0, 31].
Jeśli jednak typem lewego argumentu jest long, wtedy jedynie 6 najmłodszych bitów jest branych pod uwagę. Ponownie, tylko tyle bitów - 2^6 - służy do reprezentacji bitowej dowolnej liczby long. Tym razem stosujemy logiczny AND z maską 0x3F (= 0b111111) i przesunięcie jest wykonane w zakresie 0 do 63 włącznie.
Lewy argument typu long nie wymusza promocji argumentu prawego do long, gdyż i tak int jest wystarczający, aby określić zakres przesunięcia [0-63] (przez zastosowanie maski 0x3F).
35 00000000 00000000 00000000 00100011
31 -> 0x1f 00000000 00000000 00000000 00011111
& -----------------------------------
00000000 00000000 00000000 00000011 -> 3
-29 11111111 11111111 11111111 11100011
31 -> 0x1f 00000000 00000000 00000000 00011111
& -----------------------------------
00000000 00000000 00000000 00000011 -> 3
Interesujący wydał mi się ten drugi przykład, gdzie prawy operand -29 daje ostatecznie przesunięcie 3 bitów (!) Z takim pytaniem na egzaminie znajomości Javy można łatwo polec.Operator lewego przesunięcia ze znakiem <<
Działanie n << s to przesunięcie s bitów w n z uwzględnieniem bitu znaku, czyli zakres działania to 31 bitów dla lewego operandu typu int lub 63 dla long (odpowiednio maskowane jak opisałem wyżej). Nowe bity po prawej, które pojawią się przy przesunięciu, są wypełniane zerami.Przesunięcie w lewo ze znakiem jest równoważne iloczynowi lewego operandu przez 2 do potęgi s, tj. n * 2^s.
Przykład: 11 << 3 = 11 * 2^3 = 11 * 8 = 88
11 (binarnie) 0000 0000 0000 0000 0000 0000 0000 1011 przesunięcie o 3 0000 0000 0000 0000 0000 0000 0101 1000 wynik: 88 (dziesiętnie)Podobnie z liczbami ujemnymi.
Operator prawego przesunięcia ze znakiem >>
Działanie n >> s to przesunięcie s bitów w n z uwzględnieniem bitu znaku, czyli zakres działania to 31 bitów dla lewego operandu typu int lub 63 dla long (odpowiednio maskowane). Przy przesunięciu w prawo ze znakiem "wypełniaczem" miejsc pustych jest najważniejszy bit - bit znaku, tj. 0 dla dodatnich i 1 dla ujemnych.Dla liczb nieujemnych, przesunięcie w prawo ze znakiem jest równoważne z obliczeniem ilorazu (bez reszty) lewego operandu przez 2 do potęgi s, tj. n / 2^s.
Przykład: 11 >> 3 = 11 / 2^3 = 11 / 8 = 1
11 (binarnie) 0000 0000 0000 0000 0000 0000 0000 1011 przesunięcie o 3 0000 0000 0000 0000 0000 0000 0000 0001 wynik: 1 (dziesiętnie)Przykład: -17 >> 3 = -3
-17 (binarnie) 1111 1111 1111 1111 1111 1111 1110 1111 przesunięcie o 3 1111 1111 1111 1111 1111 1111 1111 1101 wynik: -3 (dziesiętnie)
Operator prawego przesunięcia bez znaku >>>
Działanie n >>> s to przesunięcie s bitów w n wliczając bit znaku (bit znaku traci swoją wyjątkowość i jest traktowany jak każdy inny bit).Z przesunięciem bez znaku "wypełniaczem" staje się 0, co może zamazać bit znaku i zmienić znak wyniku, który zawsze będzie dodatni.
Próbując wyrazić działanie >>> wzorem stosuje się następującą zasadę - jeśli lewy operand jest dodatni, wtedy wynik jest identyczny z prawym przesunięciem ze znakiem. Jeśli jednak lewy operand jest ujemy, wtedy wynik równa się (n >> s) + (2 << ~s). I właśnie ten wzór mnie zmroził najbardziej. Aż mnie do tej pory ciarki przechodzą :)
Przykład: 11 >>> 3 = 11 >> 3 = 11 / 2^3 = 1
11 (binarnie) 0000 0000 0000 0000 0000 0000 0000 1011 przesunięcie o 3 0000 0000 0000 0000 0000 0000 0000 0001 wynik: 1 (dziesiętnie)Przykład: -17 >>> 3 = 536870909
-17 (binarnie) 1111 1111 1111 1111 1111 1111 1110 1111 przesunięcie o 3 0001 1111 1111 1111 1111 1111 1111 1101 wynik: 536870909 (dziesiętnie)
Zastosowanie przesunięć
Operatory ze znakiem to odpowiednio mnożenie i dzielenie lewego operanda przez s-tą potęgę dwójki.Tylko skąd pomysł na >>>?! Na pewno nie zamierzam zapamiętać wzoru na wynik (chociaż przy przeglądaniu Sieci i spisywaniu wiedzy już zagnieździło się w głowie). Musi być tego jakieś sensowne uzasadnienie.
Dopiero w The Unsigned Right Shift olśniło mnie, kiedy trafiłem na możliwe zastosowanie operatora prawego przesunięcia bez znaku "shifting bits that does not represent a numeric value, e.g. pixel-based values and graphics."
I uzupełniając, w Bit twiddling in Java: The Unsigned Right Shift Operator:
Mimo, że każdy ciąg bitów o długości mniejszej niż 64 (najdłuższy typ w Javie - long) reprezentuje pewną liczbę całkowitą, to jedynie przesunięcia ze znakiem szanują reprezentację ciągu w postaci liczby - zachowują znak, a on ma znaczenie przy liczbach. W przypadku przesunięcia w prawo bez znaku >>> reprezentacja liczbowa nie ma znaczenia, a jedynie ciąg bitów - właśnie przez brak dbałości o znak liczby.
Innymi słowy, główne zastosowanie prawego przesunięcia bez znaku to sterowniki urządzeń, niskopoziomowa obsługa grafiki i formatów graficznych, pakiety komunikacyjne, kryptografia. W mojej karierze z Javą nigdy jednak nie spotkałem się z takimi zadaniami, ani chociażby minimalnego użycia operatorów przesunięcia. Zdają się być zbyt magiczne, aby miały zastosowanie nad chociażby java.lang.Math.pow(double, double). Mówi się również, że maksymalna liczba możliwych pytań z operatorów przesunięć na egzaminie z SCJP nie przekracza...jednego (!)
Zagadka: Jaki będzie wynik dowolnego przesunięcia liczby 0xFFFFFFFF (8 znaków F, tj. 32 jedynki) dla operatorów prawego przesunięcia ze znakiem?
W artykule Bitwise operation znalazłem bardzo intrygujące zachowanie przesunięć.
Zacznijmy od zagadki: Jaki będzie wynik odpowiednich przesunięć - (-12 >> 2) << 2, (-12 << 2) >> 2, (-12 >>> 2) << 2, (-12 << 2) >>> 2?
UWAGA: Przesunięcia typów mniej pojemnych, które będą rozszerzane do int - do 32 bitów, np. 8-bitowy byte rozszerzany jest do 32 bitowego int, więc przesunięcie >>> może nie mieć żadnego wpływu i należy maskować liczbę przed przesunięciem, np. (b & 0xFF) >>> 2.
Przesunięcie w prawo zawsze skutkuje "wypadaniem" liczb poza koniec (jakby wpadały w przepaść i były tracone na zawsze).
W The SCJP Tip Line Bit Shifting by Corey McGlone trafiłem na ciekawą zagadkę, która wynika bezpośrednio z powyższych reguł przesunięcia liczb całkowitych (szczególnie maskowaniem prawego operandu).
Zagadka: Jaki będzie wynik 3 >>> 32?
Na odpowiedź masz jedynie sekundę :)
Pamiętasz maskowanie prawego operandu przed wykonaniem przesunięcia? Dla int będzie to maska 0x1F, czyli 32. Czy teraz łatwiej odpowiedzieć na powyższe pytanie? To jaki będzie wynik?
Wystarczy zastosować mod 32 i masz gotową odpowiedź.
3 >>> 32 = 3 >>> (32 % 32) = 3 >>> 0 = 3
Fajne, co?! Właśnie takie cuda w Javie i w ogóle w językach programowania nakręcają mnie na dalsze ich zgłębianie. Czego i Tobie życzę w Nowym Roku 2012! :)
31 grudnia 2011
17b 79 63 7a 65 6e 69 61 20 6e 6f 77 6f 72 6f 63 7a 6e 65
Właśnie dobiega końca 2011 rok i nadeszła pora na życzenia noworoczne. Ostatnio siedzę nad reprezentacją liczb całkowitych w Javie, więc nie inaczej mogło być i dzisiaj (w końcu to taka uroczysta pora, która wymaga specjalnego potraktowania :-))
1010011 1111010 1100011 1111010 100011001 101011011 1101100 1101001 1110111 1100101 1100111 1101111 100000 1001110 1101111 1110111 1100101 1100111 1101111 100000 1010010 1101111 1101011 1110101 100000 110010 110000 110001 110010 100000 101111100 1111001 1100011 1111010 1111001 100000 1001010 1100001 1100011 1100101 1101011 100000 1001100 1100001 1110011 1101011 1101111 1110111 1110011 1101011 1101001 100000 1111010 100000 1110010 1101111 1100100 1111010 1101001 1101110 100000101 101110Do odkodowania można skorzystać z poniższej aplikacji.
package pl.japila.java7;
import java.util.Scanner;
import javax.swing.JOptionPane;
public class ZyczeniaNoworoczne {
public static void main(String... args) {
String userInput = JOptionPane.showInputDialog(null,
"Wprowadź dane do odkodowania (zastosuj zasadę Copiego & Paste)");
StringBuilder zyczeniaNoworoczne = new StringBuilder();
Scanner scanner = new Scanner(userInput);
while (scanner.hasNext()) {
zyczeniaNoworoczne.append((char) Integer.parseInt(scanner.next(), 2));
}
JOptionPane.showMessageDialog(null, zyczeniaNoworoczne);
}
}p.s. Do odkodowania tytułu zastosuj podstawę 16 w linii 17.
30 grudnia 2011
O kodzie uzupełnień do dwóch w Javie raz jeszcze
W Kod uzupełnień do dwóch w Javie (reprezentacja binarna liczb całkowitych) zaprezentowałem moją dotychczasową wiedzę na temat reprezentacji binarnej liczb całkowitych w Javie. Uważam to za mój początek w dokładniejszym rozpoznaniu tematu i nie ukrywam, że wciągnął mnie. Przypomnę tylko, że zaczęło się od zmian w Javie 7 z Fork/Join (nowe podejście do współbieżności w Javie z konstrukcjami wyższego poziomu), aby przez operator przesunięcia w prawo bez znaku >>> przejść do kodu uzupełnień do dwóch dla liczb całkowitych w Javie. Właśnie takie przejścia z jednego tematu na drugi w krótkim czasie lubię najbardziej. Nie pozwalają człowiekowi na dłuższy bezruch.
Przez ostatni tydzień dalej zgłębiałem temat i przesiedziałem sporo czasu czytając różnej maści artykuły. O większości pisałem na swoim kanale @jaceklaskowski na twitterze, więc wielu już miało przedsmak tego, o czym teraz będę pisał.
Prawie.
I już przy omawianiu dostępnych typów dostrzec można ich różną reprezentację wewnętrzną - byte, short, int i long są z bitem znaku, w przeciwieństwie do char, któremu dano wszystkie dostępne bity do dyspozycji.
"Two's complement numbers is a way to encode negative numbers into ordinary binary, such that addition still works."
Tylko o jakie dodawanie chodzi?!
Właśnie o zwykłe dodawanie binarne się rozchodzi. Jak mogłem się zorientować przeszukując materiały w Sieci, istnieje wiele systemów reprezentowania liczb całkowitych, ale czy to podwójne zero, czy konieczność rozróżniania znaku dodawanych liczb, sprawiają, że kod uzupełnienia do dwóch wydaje się być najbardziej trafnym. I taki zastosowano w Javie.
W Signed Int: Two's Complement mogłem dalej zgłębiać niuanse kodu uzupełnień do dwóch, który od tej pory będę zapisywał jako 2C (od angielskiego two's complement). Tam dowiedziałem się, że w 2C występuje jedno zero i dodawanie jest spójne dla reprezentacji bitowej z i bez znaku korzystając ze sprzętowej realizacji dodawania bitów (bez względu, czy reprezentują liczbę ze znakiem, czy bez - obie reprezentacje sprowadzane są do reprezentacji bez znaku). Ta cecha 2C jest związana z działaniem sprzętowym dodawania, a tutaj moja wiedza kończy się niezwykle szybko i na tym poprzestanę. I czuję, że więcej nie jest mi potrzebne o maszynowych rozwiązaniach.
Zadanie dla dociekliwych: wykonaj bitowe dodawanie liczb całkowitych, np. 1 i 2.
Najdłuższa liczba całkowita - typu long - ma do dyspozycji 64 bity. Pierwszy bit zarezerwowany jest dla bitu znaku (poza char).
Do uzyskania liczby ujemnej w 2C mamy 3 różne sposoby, z których najczęściej brany jest ten, który polega na odwróceniu wszystkich bitów i dodaniu jedynki, tj. -B = ~B + 1, przy założeniu, że B to dowolna liczba całkowita.
Zadanie dla dociekliwych: znajdź liczbę przeciwną do 1 korzystając z powyższego algorytmu.
I właśnie w tym momencie dociera do mnie jak interesującym jest rozpoznawanie tematu reprezentacji 2C. Dodaj 1 do liczby, której reprezentacja bitowa zawiera wyłącznie 1ki (czyli -1).
Idąc dalej, możnaby zastanowić się, co stanie się przy wyznaczaniu liczby przeciwnej dwukrotnie? Czy zachodzi zasada wyznaczania liczby przeciwnej podwójnie, która powinna wyznaczyć liczbę początkową, czyli -(-x) = x, w 2C?
Krok 1. Reprezentacja liczby całkowitej w postaci 32 bitów (dla int) lub 64 bitów (dla long). Ja pozostanę przy 32 bitach.
int n0 = 1111 1111 1111 1111 1111 1111 1111 1111 // -1
Krok 2. Odwracamy wszystkie bity
int n1 = 0000 0000 0000 0000 0000 0000 0000 0000 // 0
Krok 3. Dodajemy jedynkę
int n2 = 0000 0000 0000 0000 0000 0000 0000 0001 // 1
Zakończyłem pierwsze wyliczenie liczby przeciwnej do zadanej, czyli -1. Czy kontynuując odwracanie wyliczę wyjściową, czyli -1?
Krok 4 Reprezentacja bitowa liczby 1
int n00 = 0000 0000 0000 0000 0000 0000 0000 0001 // 1
Krok 5 Odwrócenie bitów
int n10 = 1111 1111 1111 1111 1111 1111 1111 1110 // nieistotne
Krok 6 Dodajemy jedynkę
int n20 = 1111 1111 1111 1111 1111 1111 1111 1111 // -1
I jak łatwo zauważyć n20 == n0, czyli parzyste wykonanie wyznaczania liczby odwrotnej do zadanej zawsze zwróci liczbę początkową.
Z Why computers represent signed integers using two’s complement tylko upewniłem się, że 2C jest warte poświęconego czasu i jego zrozumienie uważam od tej pory za obowiązkowe (ja już mam za sobą, więc tym łatwiej jest mi rzucać takie stwierdzenia :)).
"The representation of signed integers is the representation used by modern processors. It is called "two's complement" because to negate an integer, you subtract it from 2N. For example, to get the representation of –2 in 3-bit arithmetic, you can compute 8 – 2 = 6, and so –2 is represented in two’s complement as 6 in binary: 110."
W tym zdaniu znajduje się kolejny sposób na wyznaczanie liczb przeciwnych w 2C. Po prostu odejmujemy liczbę dodanią od 2^N, czyli dla 32 bitowych liczb typu int będzie to 1 + 32 zera w binarnej reprezentacji (wyznaczenie, a co ważniejsze, zapamiętanie tej liczby stanowi nie lada wyzwanie umysłowe, więc pozostańmy przy takim opisie).
Z Kod uzupełnień do dwóch w Javie (reprezentacja binarna liczb całkowitych) wiemy, że mnożenie
Przez ostatni tydzień dalej zgłębiałem temat i przesiedziałem sporo czasu czytając różnej maści artykuły. O większości pisałem na swoim kanale @jaceklaskowski na twitterze, więc wielu już miało przedsmak tego, o czym teraz będę pisał.
Prawie.
Liczby całkowite w Javie
Specyfikacja The Java Language Specification, Third Edition w rozdziale 4.2.1 Integral Types and Values wymienia typy całkowite z ich zakresami. W kolejności ich długości wyróżniamy: byte na 8 bitach, short na 16 bitach, int na 32 bitach i najdłuższy long na 64 bitach. Jest jeszcze całkowite char, które reprezentowane jest na 16 bitach, z tą różnicą, w porównaniu do poprzednich, że bez bitu znaku (najstarszy bit, pierwszy od lewej strony, albo ostatni z prawej).I już przy omawianiu dostępnych typów dostrzec można ich różną reprezentację wewnętrzną - byte, short, int i long są z bitem znaku, w przeciwieństwie do char, któremu dano wszystkie dostępne bity do dyspozycji.
Dodawanie i liczba przeciwna w kodzie uzupełnień do dwóch
W artykule na Wikipedii Two's complement trafiłem na zdanie, które wywarło na mnie niesamowite wrażenie i sprawiło, że zrozumiałem sens istnienia tego systemu kodowania. Uważam, że mogłoby stanowić świetne wprowadzenie dla całego artykułu, albo jedyne:"Two's complement numbers is a way to encode negative numbers into ordinary binary, such that addition still works."
Tylko o jakie dodawanie chodzi?!
Właśnie o zwykłe dodawanie binarne się rozchodzi. Jak mogłem się zorientować przeszukując materiały w Sieci, istnieje wiele systemów reprezentowania liczb całkowitych, ale czy to podwójne zero, czy konieczność rozróżniania znaku dodawanych liczb, sprawiają, że kod uzupełnienia do dwóch wydaje się być najbardziej trafnym. I taki zastosowano w Javie.
W Signed Int: Two's Complement mogłem dalej zgłębiać niuanse kodu uzupełnień do dwóch, który od tej pory będę zapisywał jako 2C (od angielskiego two's complement). Tam dowiedziałem się, że w 2C występuje jedno zero i dodawanie jest spójne dla reprezentacji bitowej z i bez znaku korzystając ze sprzętowej realizacji dodawania bitów (bez względu, czy reprezentują liczbę ze znakiem, czy bez - obie reprezentacje sprowadzane są do reprezentacji bez znaku). Ta cecha 2C jest związana z działaniem sprzętowym dodawania, a tutaj moja wiedza kończy się niezwykle szybko i na tym poprzestanę. I czuję, że więcej nie jest mi potrzebne o maszynowych rozwiązaniach.
Zadanie dla dociekliwych: wykonaj bitowe dodawanie liczb całkowitych, np. 1 i 2.
Najdłuższa liczba całkowita - typu long - ma do dyspozycji 64 bity. Pierwszy bit zarezerwowany jest dla bitu znaku (poza char).
Do uzyskania liczby ujemnej w 2C mamy 3 różne sposoby, z których najczęściej brany jest ten, który polega na odwróceniu wszystkich bitów i dodaniu jedynki, tj. -B = ~B + 1, przy założeniu, że B to dowolna liczba całkowita.
Zadanie dla dociekliwych: znajdź liczbę przeciwną do 1 korzystając z powyższego algorytmu.
I właśnie w tym momencie dociera do mnie jak interesującym jest rozpoznawanie tematu reprezentacji 2C. Dodaj 1 do liczby, której reprezentacja bitowa zawiera wyłącznie 1ki (czyli -1).
1111 1111 1111 1111 1111 1111 1111 1111 // -1 + 0000 0000 0000 0000 0000 0000 0000 0001 // 1 ------------------------------------------------------------------- 10000 0000 0000 0000 0000 0000 0000 0000 // 0 (na 33 bitach = przepełnienie!)Obie liczby są typu int. Obie mają do dyspozycji 32 bity z ostatnim bitem (licząc od lewej) zarezerwowanym na znak. Wynik również musi mieścić się w 32 bitach, ale cóż to?! Dodawanie 1 i -1 kończy się przepełnieniem - wynik wymusza 33 bity. W Javie taka sytuacja jest ukrywana przez usunięcie dodatkowego 33 bitu, aby w ten sposób otrzymać liczbę 32-bitową, która składa się wyłącznie z samych 0, a to po prostu 0 w systemie dziesiętnym. Jakież to piękne!
Idąc dalej, możnaby zastanowić się, co stanie się przy wyznaczaniu liczby przeciwnej dwukrotnie? Czy zachodzi zasada wyznaczania liczby przeciwnej podwójnie, która powinna wyznaczyć liczbę początkową, czyli -(-x) = x, w 2C?
Krok 1. Reprezentacja liczby całkowitej w postaci 32 bitów (dla int) lub 64 bitów (dla long). Ja pozostanę przy 32 bitach.
int n0 = 1111 1111 1111 1111 1111 1111 1111 1111 // -1
Krok 2. Odwracamy wszystkie bity
int n1 = 0000 0000 0000 0000 0000 0000 0000 0000 // 0
Krok 3. Dodajemy jedynkę
int n2 = 0000 0000 0000 0000 0000 0000 0000 0001 // 1
Zakończyłem pierwsze wyliczenie liczby przeciwnej do zadanej, czyli -1. Czy kontynuując odwracanie wyliczę wyjściową, czyli -1?
Krok 4 Reprezentacja bitowa liczby 1
int n00 = 0000 0000 0000 0000 0000 0000 0000 0001 // 1
Krok 5 Odwrócenie bitów
int n10 = 1111 1111 1111 1111 1111 1111 1111 1110 // nieistotne
Krok 6 Dodajemy jedynkę
int n20 = 1111 1111 1111 1111 1111 1111 1111 1111 // -1
I jak łatwo zauważyć n20 == n0, czyli parzyste wykonanie wyznaczania liczby odwrotnej do zadanej zawsze zwróci liczbę początkową.
Z Why computers represent signed integers using two’s complement tylko upewniłem się, że 2C jest warte poświęconego czasu i jego zrozumienie uważam od tej pory za obowiązkowe (ja już mam za sobą, więc tym łatwiej jest mi rzucać takie stwierdzenia :)).
"The representation of signed integers is the representation used by modern processors. It is called "two's complement" because to negate an integer, you subtract it from 2N. For example, to get the representation of –2 in 3-bit arithmetic, you can compute 8 – 2 = 6, and so –2 is represented in two’s complement as 6 in binary: 110."
W tym zdaniu znajduje się kolejny sposób na wyznaczanie liczb przeciwnych w 2C. Po prostu odejmujemy liczbę dodanią od 2^N, czyli dla 32 bitowych liczb typu int będzie to 1 + 32 zera w binarnej reprezentacji (wyznaczenie, a co ważniejsze, zapamiętanie tej liczby stanowi nie lada wyzwanie umysłowe, więc pozostańmy przy takim opisie).
Z Kod uzupełnień do dwóch w Javie (reprezentacja binarna liczb całkowitych) wiemy, że mnożenie
package pl.japila.java7;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Test;
public class TwoCDemo {
@Test
public void test() {
long oneAnd32Zeros = (long)1 << 32; // 0x1_FFFF_FFFF - int overflow!
int three = 3; // 0b11
int minusThree = (int)(oneAnd32Zeros - three);
assertThat(minusThree, is(-3));
}
}
Jako podsumowanie, warto zwrócić uwagę na użycie operatora przesunięcia w lewo, aby uzyskać liczbę podwójnie większą od największej w zakresie int. Nie chciałem pisać tej liczby dziesiętnie, a (czego nie wiedziałem wcześniej) jej reprezentacja w notacji bitowej (z 0b), ósemkowej (z wiodącym 0), albo szesnastkowo (z 0x) nie jest możliwa - są one zarezerwowane wyłącznie dla typów int i mniejszych.23 grudnia 2011
Kod uzupełnień do dwóch w Javie (reprezentacja binarna liczb całkowitych)
Wszystko zaczęło się od mojego przedstawienia zmian w Javie 7, w pakiecie java.util.concurrent podczas 84 spotkania Warszawa JUG - Warszawski Eclipse DemoCamp 2011 - Java 7, JavaFX i Eclipse (prezentacja do pobrania jako JacekLaskowski-EclipseDemoCamp2011-ConcurrencyUtilitiesJava7-2011.11.08.pdf).
Wtedy zaczęła się moja przygoda ze szkieletem Fork/Join. Rozwiązywanie problemu przez zrównoleglanie jego mniejszych składowych wymaga właściwego sposobu podziału (zwykle po połowie) i tak do ustalonego, minimalnego poziomu jego złożoności, a właściwie braku, po którym rozwiązanie można obliczyć "siłowo" (element po elemencie, liniowo).
Kwestią, z którym zwykle zmagają się programiści korzystający z Fork/Join (czy dowolnego problemu rozwiązywanego przez algorytmy typu "dziel i zwycieżaj") to, w jaki sposób dzielić, aby samo dzielenie nie było na tyle skomplikowane, że zysk ze zrównoleglenia zostanie przez niego skonsumowany i ostatecznie wyjdziemy na przysłowiowe "zero".
I tu pojawia się przyczynek do tego wpisu - operator przesunięcia bez znaku w prawo >>> (ang. unsigned right shift) w Javie.
Nie jest to niczym odkrywczym w Javie 7, ale przy Fork/Join nabrał większego znaczenia. Mówiąc wprost, ja na niego zwróciłem uwagę właśnie przy Fork/Join. I tak kończy się w zasadzie rola Fork/Join, które nie będzie już przywoływane, bo posłużył wyłącznie jako tło do rozpoznania operatora przesunięcia i jak się później okazało reprezentacji liczb całkowitych w Javie.
Weźmy następujący problem: W jaki sposób wyznaczyć połowę pewnej liczby nieujemnej x?
Liczba dowolna, acz ustalona, x (teraz dopiero dojrzałem, aby dostrzec piękno tych słów) może być liczbą elementów w tablicy, w którym znajdują się elementy do przetworzenia.
Operator przesunięcia w prawo bez znaku >>> jest złożeniem << i >> w zależności od znaku lewego argumentu - dodatni to >>, a ujemny...cóż...tu sprawa się komplikuje i wygląda (n>>s)+(2<<~s) przy założeniu, że rozważamy n>>>s.
Pamiętam, kiedy dostrzegłem (a właściwie pokazano mi palcem), że n << 1 to po prostu n * 2. Innych zastosowań przesunięcia w lewo jeszcze nie odkryłem, ale pewnie są równie zabójcze dla mojego serca :)
Wtedy zacząłem zgłębiać reprezentację liczb całkowitych w Javie, bo w końcu działanie tych operatorów polega na przesunięciach binarnych.
Zadanie wprowadzające: Czy znasz zapis binarny ujemnej liczby całkowitej w Javie? Niech to będzie -1 lub Integer.MIN_VALUE.
Właśnie wtedy zreflektowałem się, jak niewiele wiem na ten temat. Niewiele?! Delikatnie powiedziane. Nic nie wiem! Zabrałem się za lekturę specyfikacji języka Java (tutaj niewiele znalazłem poza ogólnikami), aby skończyć na pojęciach reprezentacjach liczb ze znakiem i bez oraz kod uzupełnień do dwóch (ang. two's complement).
I tak od rozpoznawania Fork/Join przeszedłem do operatora przesunięcia, aby skończyć na reprezentacji liczb całkowitych w Javie.
W ten sposób powstała moja implementacja zamiany binarnego ciągu znaków (bez wiodącego 0b, które doszło w Javie 7) do postaci liczby całkowitej w zapisie dziesiętnym. Klasa nie jest krótka, ale starałem się dobierać nazwy do ich przeznaczenia, więc sądzę, że stosunkowo łatwo będzie zorientować się, co miałem na myśli.
Przede mną analiza kodów źródłowych java.lang.Integer, która dostarcza większości z poniższych metod. Niedługo więcej w temacie, bo nie czuję, abym go wyczerpał (a mam wrażenie, że jedynie zdrapałem wierzchnią warstwę). Oczekuję uwag i wskazówek od życzliwego czytelnika (w czasie świąt niegodnym nie podzielić się miłym słówkiem).
Gdybyśmy już się nie widzieli, życzę najlepszego z okazji nadchodzących Świąt Bożego Narodzenia. Baw się i świętuj, aby wypoczęty wrócić do dalszych prac poznawczych.
Wtedy zaczęła się moja przygoda ze szkieletem Fork/Join. Rozwiązywanie problemu przez zrównoleglanie jego mniejszych składowych wymaga właściwego sposobu podziału (zwykle po połowie) i tak do ustalonego, minimalnego poziomu jego złożoności, a właściwie braku, po którym rozwiązanie można obliczyć "siłowo" (element po elemencie, liniowo).
Kwestią, z którym zwykle zmagają się programiści korzystający z Fork/Join (czy dowolnego problemu rozwiązywanego przez algorytmy typu "dziel i zwycieżaj") to, w jaki sposób dzielić, aby samo dzielenie nie było na tyle skomplikowane, że zysk ze zrównoleglenia zostanie przez niego skonsumowany i ostatecznie wyjdziemy na przysłowiowe "zero".
I tu pojawia się przyczynek do tego wpisu - operator przesunięcia bez znaku w prawo >>> (ang. unsigned right shift) w Javie.
Nie jest to niczym odkrywczym w Javie 7, ale przy Fork/Join nabrał większego znaczenia. Mówiąc wprost, ja na niego zwróciłem uwagę właśnie przy Fork/Join. I tak kończy się w zasadzie rola Fork/Join, które nie będzie już przywoływane, bo posłużył wyłącznie jako tło do rozpoznania operatora przesunięcia i jak się później okazało reprezentacji liczb całkowitych w Javie.
Weźmy następujący problem: W jaki sposób wyznaczyć połowę pewnej liczby nieujemnej x?
Liczba dowolna, acz ustalona, x (teraz dopiero dojrzałem, aby dostrzec piękno tych słów) może być liczbą elementów w tablicy, w którym znajdują się elementy do przetworzenia.
Operator przesunięcia w prawo bez znaku >>> jest złożeniem << i >> w zależności od znaku lewego argumentu - dodatni to >>, a ujemny...cóż...tu sprawa się komplikuje i wygląda (n>>s)+(2<<~s) przy założeniu, że rozważamy n>>>s.
Pamiętam, kiedy dostrzegłem (a właściwie pokazano mi palcem), że n << 1 to po prostu n * 2. Innych zastosowań przesunięcia w lewo jeszcze nie odkryłem, ale pewnie są równie zabójcze dla mojego serca :)
Wtedy zacząłem zgłębiać reprezentację liczb całkowitych w Javie, bo w końcu działanie tych operatorów polega na przesunięciach binarnych.
Zadanie wprowadzające: Czy znasz zapis binarny ujemnej liczby całkowitej w Javie? Niech to będzie -1 lub Integer.MIN_VALUE.
Właśnie wtedy zreflektowałem się, jak niewiele wiem na ten temat. Niewiele?! Delikatnie powiedziane. Nic nie wiem! Zabrałem się za lekturę specyfikacji języka Java (tutaj niewiele znalazłem poza ogólnikami), aby skończyć na pojęciach reprezentacjach liczb ze znakiem i bez oraz kod uzupełnień do dwóch (ang. two's complement).
I tak od rozpoznawania Fork/Join przeszedłem do operatora przesunięcia, aby skończyć na reprezentacji liczb całkowitych w Javie.
W ten sposób powstała moja implementacja zamiany binarnego ciągu znaków (bez wiodącego 0b, które doszło w Javie 7) do postaci liczby całkowitej w zapisie dziesiętnym. Klasa nie jest krótka, ale starałem się dobierać nazwy do ich przeznaczenia, więc sądzę, że stosunkowo łatwo będzie zorientować się, co miałem na myśli.
Przede mną analiza kodów źródłowych java.lang.Integer, która dostarcza większości z poniższych metod. Niedługo więcej w temacie, bo nie czuję, abym go wyczerpał (a mam wrażenie, że jedynie zdrapałem wierzchnią warstwę). Oczekuję uwag i wskazówek od życzliwego czytelnika (w czasie świąt niegodnym nie podzielić się miłym słówkiem).
Gdybyśmy już się nie widzieli, życzę najlepszego z okazji nadchodzących Świąt Bożego Narodzenia. Baw się i świętuj, aby wypoczęty wrócić do dalszych prac poznawczych.
package pl.japila.java7;
public class BinaryToDecimalIntDemo {
static final char ZERO = '0';
static final char ONE = '1';
public static void main(String[] args) {
for (int number = Integer.MIN_VALUE; number <= Integer.MIN_VALUE + 0xFFFF; number++) {
String negativeNumber = Integer.toBinaryString(number);
int intInDecimal = BinaryToDecimalIntDemo.getIntInDecimal(negativeNumber);
assert number == intInDecimal;
}
for (int number = Integer.MAX_VALUE; number >= Integer.MAX_VALUE - 0xFFFF; number--) {
String negativeNumber = Integer.toBinaryString(number);
int intInDecimal = BinaryToDecimalIntDemo.getIntInDecimal(negativeNumber);
assert number == intInDecimal;
}
}
static int getIntInDecimal(String binary) {
int sum = 0;
boolean negative = false;
char[] bits = binary.toCharArray();
if (bits.length == Integer.SIZE) {
if (isNegative(bits)) {
negative = true;
bits = subtractOne(invertBits(removeSignBit(bits)));
}
}
for (int i = bits.length - 1, j = 0; i >= 0; i--, j++) {
sum += calculateValueInBinaryRepAt(j, bits[i]);
}
return negative ? (sum == 0 ? Integer.MIN_VALUE : -sum) : sum;
}
private static char[] subtractOne(final char[] bits) {
char[] newBits = new char[bits.length];
System.arraycopy(bits, 0, newBits, 0, newBits.length);
int i = newBits.length - 1;
if (newBits[i] == ONE) {
newBits[i] = ZERO;
} else {
newBits[i] = ONE;
for (int j = i - 1; j >= 0; j--) {
if (newBits[j] == ZERO) {
newBits[j] = ONE;
} else {
newBits[j] = ZERO;
break;
}
}
}
return newBits;
}
private static char[] invertBits(char[] bits) {
char[] newBits = new char[bits.length];
System.arraycopy(bits, 0, newBits, 0, newBits.length);
for (int i = 0; i < newBits.length; i++) {
newBits[i] = (newBits[i] == ZERO ? ONE : ZERO);
}
return newBits;
}
private static char[] removeSignBit(char[] bits) {
char[] newBits = new char[bits.length - 1];
System.arraycopy(bits, 1, newBits, 0, newBits.length);
return newBits;
}
private static boolean isNegative(char[] bits) {
return bits.length == Integer.SIZE && bits[0] == ONE;
}
private static double calculateValueInBinaryRepAt(int i, char c) {
return Math.pow(2, i) * Character.getNumericValue(c);
}
}
10 lutego 2007
Ciekawostki języka Java
Pracuję z Javą od pierwszych dni jej opublikowania. Ostatnio zdobyłem certyfikat SCJP 5.0 i wydawałoby się, że to wystarczy do zagwarantowania dogłębnej znajomości języka. Ale jak pokazały wydarzenia dnia wczorajszego, niekoniecznie.
Pierwsza ciekawostka dotyczyła dostępu do zmiennych i metod prywatnych (składowych klasy oznaczonych słowem kluczowym private). Skoro prywatnych to wydawałoby się, że sprawa jasna - nikt poza ich klasą macierzystą nie ma prawa ich "dotknąć". Ale, jak się okazało, nie jest to precyzyjna definicja.
Możnaby zadać pytanie, jak bardzo utrzymywana jest prywatność składowych klasy (pól i metod)? Nie sądzę, aby nie znalazła się przynajmniej jedna osoba programująca na codzień w Javie, która nie złapałaby się za głowę, kiedy ujrzałaby działanie poniższego programu. Czy zapytana, potrafiłba odpowiedzieć jaki będzie wynik kompilacji i przyjmując, że kompilacja się zakończy poprawnie, jaki będzie wynik działania poniższego programu? Uwagę powinien przyciągnąć dostęp do prywatnego pola i metody w metodzie statycznej main.
Skoro prywatne składowe klasy, to prywatne, tak? Otóż nie! Wszystko zależy od tego, kto dostępuje składowych prywatnych klasy. W Javie (mówi się, że podobnie jak w C++, ale przeciwnie do Smalltalk) dostęp do zmiennych prywatnych jest niemożliwy, za wyjątkiem sytuacji, w której wykonywane operacje są inicjowane przez klasę macierzystą. Dodać do tego należy, że dostęp do prywatnej składowej dowolnego egzemplarza klasy X wewnątrz metod klasy X jest również możliwy (!) Dla mnie taki dostęp był dostępem z zewnątrz, pomimo, że w klasie deklarującej ową prywatną składową.
Wytłumaczono mi to w ten sposób, że prywatne elementy służą ukrywaniu implementacji, wewnętrznej realizacji działania klasy i to ona sama podejmuje decyzje odnośnie prywatnych składowych. Może zatem podjąć decyzję o modyfikacji swoich własnych prywatnych składowych,w tym i dowolnego egzemplarza siebie samej.
Cała literatura nt. temat brzmi w takim stylu (wycinek z podręcznika Javy - The Java Tutorial sekcja Controlling Access to Members of a Class):
The private modifier specifies that the member can only be accessed in its own class.
Jak widać z zaprezentowanego przykładu, interpretacja powyższego wycinka może nie być satysfakcjonująca. Dla mnie nie była. Na szczęście specyfikacja języka Java - The Java Language Specification - w sekcji 6.6.1 Determining Accessibility zawiera precyzyjne wytłumaczenie:
If the member or constructor is declared private, then access is permitted if and only if it occurs within the body of the top level class (§7.6) that encloses the declaration of the member or constructor.
Może ranga dokumentu przekonuje mnie do tego wytłumaczenia, albo faktyczna jego precyzyjność, ale dla mnie wystarczająco tłumaczy zachowanie prywatnych składowych klas i ich widoczności w składowych klasy, nawet jeśli dotykają one egzemplarzy, ale tylko typu takiego samego, do jakiego one same należą.
Można zadać pytanie, a jak będzie w przypadku interfejsów? ;-) Uwaga: kawał!
Druga ciekawostka związana jest z wszechobecnymi adnotacjami, a szczególnie ich wykorzystaniu w tworzeniu aplikacji opartych o Java EE. Przedstawia się ją, a szczególnie ostatnio mnie absorbującą specyfikację EJB3 (wraz z JPA 1.0), jako bezinwazyjną, tzn. wymagania specyfikacji nie wprowadzają do klas żadnych zmian, które uniemożliwiłyby ich użycie poza środowiskiem serwera aplikacyjnego Java EE - zero wymaganych interfejsów, włączania do zadanej hierarchi dziedziczenia, itp.
Pytanie jakie mi zadano podczas ostatniej prezentacji specyfikacji EJB 3.0 dotyczyło obecności adnotacji w klasie, będącej z punktu widzenia serwera aplikacyjnego komponentem EJB (użycie adnotacji @Stateless) i jej uruchomieniu poza serwerem aplikacyjnym (co zdejmuje jej "specjalność").
Adnotacje wymagają importu klas je realizujących, np. @EJB będzie wymagało importu klasy javax.ejb.EJB. Pytanie jakie się pojawia to obecność klasy podczas kompilacji i uruchomienia (można postawić ogólniejszy problem, nie dotykający adotacji, a poruszający jedynie same deklaracje import). W którym momencie, klasa w imporcie będzie wymagana? Podczas kompilacji i uruchamiana, czy wyłącznie podczas kompilacji, a może jednak wyłącznie podczas uruchamiania? Dla zobrazowania problemu przedstawiam klasę, która wykorzystuje adnotację @EJB.
Pytanie 2: Czy klasa może zostać uruchomiona bez dostępności klasy EJB?
Odpowiedź dostarczają poniższe sesje.
Pierwsza ciekawostka dotyczyła dostępu do zmiennych i metod prywatnych (składowych klasy oznaczonych słowem kluczowym private). Skoro prywatnych to wydawałoby się, że sprawa jasna - nikt poza ich klasą macierzystą nie ma prawa ich "dotknąć". Ale, jak się okazało, nie jest to precyzyjna definicja.
Możnaby zadać pytanie, jak bardzo utrzymywana jest prywatność składowych klasy (pól i metod)? Nie sądzę, aby nie znalazła się przynajmniej jedna osoba programująca na codzień w Javie, która nie złapałaby się za głowę, kiedy ujrzałaby działanie poniższego programu. Czy zapytana, potrafiłba odpowiedzieć jaki będzie wynik kompilacji i przyjmując, że kompilacja się zakończy poprawnie, jaki będzie wynik działania poniższego programu? Uwagę powinien przyciągnąć dostęp do prywatnego pola i metody w metodzie statycznej main.
package accesscontrol;Spróbuj odpowiedzieć na pytania samodzielnie, zanim podam trochę dodatkowych informacji.
public class Private {
private int privateMember;
public Private(int privateMember) {
this.privateMember = privateMember;
}
public static void modifyPrivateField(Private p) {
p.privateMember *= 2;
}
private void callPrivateMethod() {
System.out.println("Prywatna metoda wywołana! Na pewno?");
}
public static void main(String[] args) {
Private p = new Private(5);
System.out.println("p.privateMember (przed zmianą) = " + p.privateMember);
modifyPrivateField(p);
System.out.println("p.privateMember (po zmianie) = " + p.privateMember);
p.callPrivateMethod();
}
}
Skoro prywatne składowe klasy, to prywatne, tak? Otóż nie! Wszystko zależy od tego, kto dostępuje składowych prywatnych klasy. W Javie (mówi się, że podobnie jak w C++, ale przeciwnie do Smalltalk) dostęp do zmiennych prywatnych jest niemożliwy, za wyjątkiem sytuacji, w której wykonywane operacje są inicjowane przez klasę macierzystą. Dodać do tego należy, że dostęp do prywatnej składowej dowolnego egzemplarza klasy X wewnątrz metod klasy X jest również możliwy (!) Dla mnie taki dostęp był dostępem z zewnątrz, pomimo, że w klasie deklarującej ową prywatną składową.
Wytłumaczono mi to w ten sposób, że prywatne elementy służą ukrywaniu implementacji, wewnętrznej realizacji działania klasy i to ona sama podejmuje decyzje odnośnie prywatnych składowych. Może zatem podjąć decyzję o modyfikacji swoich własnych prywatnych składowych,w tym i dowolnego egzemplarza siebie samej.
Cała literatura nt. temat brzmi w takim stylu (wycinek z podręcznika Javy - The Java Tutorial sekcja Controlling Access to Members of a Class):
The private modifier specifies that the member can only be accessed in its own class.
Jak widać z zaprezentowanego przykładu, interpretacja powyższego wycinka może nie być satysfakcjonująca. Dla mnie nie była. Na szczęście specyfikacja języka Java - The Java Language Specification - w sekcji 6.6.1 Determining Accessibility zawiera precyzyjne wytłumaczenie:
If the member or constructor is declared private, then access is permitted if and only if it occurs within the body of the top level class (§7.6) that encloses the declaration of the member or constructor.
Może ranga dokumentu przekonuje mnie do tego wytłumaczenia, albo faktyczna jego precyzyjność, ale dla mnie wystarczająco tłumaczy zachowanie prywatnych składowych klas i ich widoczności w składowych klasy, nawet jeśli dotykają one egzemplarzy, ale tylko typu takiego samego, do jakiego one same należą.
Można zadać pytanie, a jak będzie w przypadku interfejsów? ;-) Uwaga: kawał!
Druga ciekawostka związana jest z wszechobecnymi adnotacjami, a szczególnie ich wykorzystaniu w tworzeniu aplikacji opartych o Java EE. Przedstawia się ją, a szczególnie ostatnio mnie absorbującą specyfikację EJB3 (wraz z JPA 1.0), jako bezinwazyjną, tzn. wymagania specyfikacji nie wprowadzają do klas żadnych zmian, które uniemożliwiłyby ich użycie poza środowiskiem serwera aplikacyjnego Java EE - zero wymaganych interfejsów, włączania do zadanej hierarchi dziedziczenia, itp.
Pytanie jakie mi zadano podczas ostatniej prezentacji specyfikacji EJB 3.0 dotyczyło obecności adnotacji w klasie, będącej z punktu widzenia serwera aplikacyjnego komponentem EJB (użycie adnotacji @Stateless) i jej uruchomieniu poza serwerem aplikacyjnym (co zdejmuje jej "specjalność").
Adnotacje wymagają importu klas je realizujących, np. @EJB będzie wymagało importu klasy javax.ejb.EJB. Pytanie jakie się pojawia to obecność klasy podczas kompilacji i uruchomienia (można postawić ogólniejszy problem, nie dotykający adotacji, a poruszający jedynie same deklaracje import). W którym momencie, klasa w imporcie będzie wymagana? Podczas kompilacji i uruchamiana, czy wyłącznie podczas kompilacji, a może jednak wyłącznie podczas uruchamiania? Dla zobrazowania problemu przedstawiam klasę, która wykorzystuje adnotację @EJB.
package annotation;Pytanie 1: Czy klasa skompiluje się bez dostępności klasy EJB?
import javax.ejb.EJB;
public class MissingAnnotationAtRuntime {
@EJB
private Object ejbRef;
public static void main(String[] args) {
System.out.println("Prezentacja programowania z adnotacjami bez nich podczas uruchomienia.");
}
}
Pytanie 2: Czy klasa może zostać uruchomiona bez dostępności klasy EJB?
Odpowiedź dostarczają poniższe sesje.
$ java -versionoraz
java version "1.5.0_08"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_08-b03)
Java HotSpot(TM) Client VM (build 1.5.0_08-b03, mixed mode)
$ echo $CLASSPATH
$ javac annotation/MissingAnnotationAtRuntime.java
annotation/MissingAnnotationAtRuntime.java:3: package javax.ejb does not exist
import javax.ejb.EJB;
^
annotation/MissingAnnotationAtRuntime.java:7: cannot find symbol
symbol : class EJB
location: class annotation.MissingAnnotationAtRuntime
@EJB
^
2 errors
$ echo $CLASSPATHZaskakujące? Dla mnie już nie!
$ java -cp bin annotation.MissingAnnotationAtRuntime
Prezentacja programowania z adnotacjami bez nich podczas uruchomienia.
Subskrybuj:
Posty (Atom)


