Якая мэта знака правага аператара зруху «>>>» у Java?

I understand what the unsigned right shift operator ">>>" in Java does, but why do we need it, and why do we not need a corresponding unsigned left shift operator?

27
Магчымы дублікат Розніца паміж >>> і >>
дададзена аўтар Amit G, крыніца

6 адказы

The >>> operator lets you treat int and long as 32- and 64-bit unsigned integral types, which are missing from the Java language.

Гэта карысна, калі вы пераносіце нешта, што не ўяўляе лікавае значэнне. Напрыклад, вы маглі б прадставіць чорна-белы біт малюнка карты, выкарыстоўваючы 32-бітную Int s, дзе кожны Int кадуе 32 пікселя на экране. Калі вам трэба пракруціць малюнак направа, вы аддалі перавагу б біты ў злева ад Int стаць нулямі, так што вы можаце лёгка змясціць біты з суседняга Int s:

 int shiftBy = 3;
 int[] imageRow = ...
 int shiftCarry = 0;
//The last shiftBy bits are set to 1, the remaining ones are zero
 int mask = (1 << shiftBy)-1;
 for (int i = 0 ; i != imageRow.length ; i++) {
    //Cut out the shiftBits bits on the right
     int nextCarry = imageRow & mask;
    //Do the shift, and move in the carry into the freed upper bits
     imageRow[i] = (imageRow[i] >>> shiftBy) | (carry << (32-shiftBy));
    //Prepare the carry for the next iteration of the loop
     carry = nextCarry;
 }

The code above does not pay attention to the content of the upper three bits, because >>> operator makes them

There is no corresponding << operator because left-shift operations on signed and unsigned data types are identical.

25
дададзена
Што вы маеце на ўвазе пад «лячыць»? Я выкарыстоўваю 32-бітныя значэння без знака ў маім кодзе ўвесь час, і я ніколі не выкарыстоўваю >>> аператара.
дададзена аўтар Tyler Durden, крыніца
@TylerDurden Без кода <>>>> (напрыклад, пры выкарыстанні <�код >>> ) азначаў біт (AKA «знакавы біт») у 32-бітнай або 64-бітнай значэнне набывае асаблівае стаўленне - ён атрымлівае прайграны ў новым знакавага біта пасля зруху, таму знак значэння пасля <�коды >>> зрух застаецца тым жа самым. Гэта адрозніваецца для беззнаковых тыпаў у мовах, якія маюць іх (напрыклад, C/C ++ або C #). У гэтых мовах ўтрыманне знакавага біта кантралюецца тыпам першага аперанда. Паколькі няма беззнаковых тыпаў у Java, мова ў канчатковым выніку ўвядзенне спецыяльнага аператара для знака «зрушвае направа».
дададзена аўтар dasblinkenlight, крыніца

>>> is also the safe and efficient way of finding the rounded mean of two (large) integers:

int mid = (low + high) >>> 1;

Калі цэлыя лікі высокі і нізкі блізкія да самай буйной машыны цэлае, вышэй будзе правільным, але

int mid = (low + high)/2;

можа атрымаць няправільны вынік з-за перапаўнення.

Вось прыклад выкарыстання , выпраўленне памылкі ў наіўным бінарным пошуку.

12
дададзена
Добрая інфармацыя, каб знайсці сярэдняе. але «будзе выклікаць выключэння» - не будзе выклікаць выключэння, але прывядзе да нечаканых вынікаў з-за перапаўнення
дададзена аўтар Mani, крыніца
Выдатнае тлумачэнне! Joshua Bloch прывёў прыклад менавіта такога выкарыстання ў « Extra, Extra - Read All About It: Амаль усе бінарныя Ператрусы і Mergesorts зламаныя ", выпраўленне памылкі ў наіўным бінарным пошуку.
дададзена аўтар Nils von Barth, крыніца

The signed right-shift operator is useful if one has an int that represents a number and one wishes to divide it by a power of two, rounding toward negative infinity. This can be nice when doing things like scaling coordinates for display; not only is it faster than division, but coordinates which differ by the scale factor before scaling will differ by one pixel afterward. If instead of using shifting one uses division, that won't work. When scaling by a factor of two, for example, -1 and +1 differ by two, and should thus differ by one afterward, but -1/2=0 and 1/2=0. If instead one uses signed right-shift, things work out nicely: -1>>1=-1 and 1>>1=0, properly yielding values one pixel apart.

Беззнаковый аператар карысны альбо ў тых выпадках, калі або ўваход, як чакаецца, мець роўна адзін біт ўстаноўлена і адзін захоча вынік, каб зрабіць гэта, а таксама, або ў тых выпадках, калі адзін будзе выкарыстоўваць цыкл, каб вывесці ўсе біты ў слове і хоча, каб спыніць чыста. Напрыклад:

void processBitsLsbFirst(int n, BitProcessor whatever)
{
  while(n != 0)
  {
    whatever.processBit(n & 1);
    n >>>= 1;
  }
}

Калі код быў выкарыстоўваць падпісаную аперацыю зруху направа і прапускаў адмоўнае значэнне, то яно будзе выводзіць 1-х да бясконцасці. З аператарам без знака правага зруху, аднак, найбольш значны біт сканчаецца інтэрпрэтуецца гэтак жа, як і любы іншы.

The unsigned right-shift operator may also be useful when a computation would, arithmetically, yield a positive number between 0 and 4,294,967,295 and one wishes to divide that number by a power of two. For example, when computing the sum of two int values which are known to be positive, one may use (n1+n2)>>>1 without having to promote the operands to long. Also, if one wishes to divide a positive int value by something like pi without using floating-point math, one may compute ((value*5468522205L) >>> 34) [(1L<<34)/pi is 5468522204.61, which rounded up yields 5468522205]. For dividends over 1686629712, the computation of value*5468522205L would yield a "negative" value, but since the arithmetically-correct value is known to be positive, using the unsigned right-shift would allow the correct positive number to be used.

3
дададзена

У асноўным гэта звязана са знакам (Numberic зрухі) або без знака зрухаў (звычайна звязаныя піксель рэчы).

Since the left shift, doesn't deal with the sign bit anyhow, it's the same thing (<<< and <<)...

Either way I have yet to meet anyone that needed to use the >>>, but I'm sure they are out there doing amazing things.

As you have just seen, the >> operator automatically fills the high-order bit with its previous contents each time a shift occurs. This preserves the sign of the value. However, sometimes this is undesirable. For example, if you are shifting something that does not represent a numeric value, you may not want sign extension to take place. This situation is common when you are working with pixel-based values and graphics. In these cases you will generally want to shift a zero into the high-order bit no matter what its initial value was. This is known as an unsigned shift. To accomplish this, you will use java’s unsigned, shift-right operator,>>>, which always shifts zeros into the high-order bit.

Далейшае чытанне:

http://henkelmann.eu/2011/02/01/java_the_unsigned_right_shift_operator

http://www.java-samples.com/showtutorial.php?tutorialid=60

3
дададзена
Калі вы пераносіце код C, што робіць зрух направа на цэлых лікаў без знака, то лепш выкарыстаць >>> або атрымаць няправільныя вынікі.
дададзена аўтар Ingo, крыніца
На самай справе, няма <<< аператар у Java.
дададзена аўтар Ted Hopp, крыніца
Сапраўды. Я каментаваў на вашым фразіроўкі, якія маглі б прапанаваць у адваротным выпадку пачаткоўцу: <�я> «гэта тое ж самае ( <<< і <<
дададзена аўтар Ted Hopp, крыніца
няма неабходнасці для аператара <<< на ўсіх :)
дададзена аўтар Dory Zidon, крыніца

A normal right shift >> of a negative number will keep it negative. I.e. the sign bit will be retained.

An unsigned right shift >>> will shift the sign bit too, replacing it with a zero bit.

Там няма неабходнасці мець эквівалентны зрух налева, таму што ёсць толькі адзін знакавы біт, і гэта самы левы біт, так што перашкаджае толькі пры зруху направа.

Па сутнасці, розніца ў тым, што адзін захоўвае бітая знак, іншыя зрухі ў нулях замяніць знакавы біт.

Для станоўчых лікаў яны дзейнічаюць аднолькава.

For an example of using both >> and >>> see BigInteger shiftRight.

1
дададзена
Так, як я ўжо сказаў, я ведаю, што ён робіць. ЧАМУ? Якая сітуацыя, калі вы клапоціцеся пра гэта?
дададзена аўтар Tyler Durden, крыніца
@TylerDurden - У BigInteger , напрыклад, ён выкарыстоўвае масіў цэлых лікаў для захоўвання яго нумара. Для зруху направа яна будзе выкарыстоўваць <�код >>>> для зруху направа ўсё, але найбольш значная колькасць.
дададзена аўтар OldCurmudgeon, крыніца

У вобласці Java найбольш тыповых прыкладанняў спосаб пазбегнуць перапаўнення заключаецца ў выкарыстанні адліўку або Big Integer, такія як INT доўга ў папярэдніх прыкладах.

int hiint = 2147483647;
System.out.println("mean hiint+hiint/2 = " + ( (((long)hiint+(long)hiint)))/2);
System.out.println("mean hiint*2/2 = " + ( (((long)hiint*(long)2)))/2);

BigInteger bhiint = BigInteger.valueOf(2147483647);
System.out.println("mean bhiint+bhiint/2 = " + (bhiint.add(bhiint).divide(BigInteger.valueOf(2))));
0
дададзена