Please explain how below code is working, as we know byte data type in java allows a range from -128 to 127.
My code snippet is:
public class DataTypes {
public static void main(String args[]){
byte b = (byte)140;
System.out.println(b);
}
}
Output: -116
A Byte will have 8 bits in its memory space. Using this space it can hold 256 values. This will span from -128 to 127 including 0. To support this spanning, a Byte value will be represented in binary format using two's compliment method. The MSB (Most Significant Bit) will determine the sign (0 - positive & 1 - negative) of the value.
If we consider the case of 140, it will be clear if we represent 140 in binary format which is 1000 1100. Remember the MSB will determine the sign and a 1 means the number is a negative value and 0 means it's a positive value. In this case we have a 1 making it a negative number.
If we apply reverse process to this two's compliment, we will first get 0111 0011 by inverting all the 1s to 0s and 0s to 1s. Then by adding 1, we get 0111 0100 which is equal to 116 in decimal format. But the MSB said it's a negative value, hence -116
Maybe this will help explain. The uppermost bit determines the 'sign'(positive or negative) of a variable. Since Java does not have an explicit 'unsigned' integer form the best you can do is apply a 'bitwise' operation to the item in advance of displaying it force it to be considered 'unsigned'...or you can display it using it's hex format which is inherently unsigned.
public class DataTypes {
public static void main(String args[]){
byte b = (byte)140;
System.out.println("hex value = "+String.format("0x%02X",b)+"\n");
System.out.println("dec value = "+String.format("%d",(b & 0xff))+"\n");
}
}
and a result of
hex value = 0x8C
dec value = 140
140 is a int. It is converted to a byte, according to the Java Language Specification, section 5.1.3, as follows:
A narrowing conversion of a signed integer to an integral type T simply discards all but the n lowest order bits
The lowest 8 bits of the int with value 140 are 10001100, which is the correct value for the byte, but which is negative since its highest bit, the sign bit, is 1.
I think, when you cast the integer (140) to byte, the integer value converted into unsigned byte then b variable value is converted into signed byte. The number 140 considered as unsigned value because the number has not sign value indicator (+ or -)
Note about unsigned byte (the right value is representation (in file byte(s)) of left value):
0-127 = 0-127
(-128) - (-1) = 128 - 255
Related
Why isn't there any function in the standard library of Kotlin/Java for taking the absolute value of a Byte/byte variable? I'm I missing something?
Math.abs() is only defined for int, long, double and float.
For context: in the audio world you can run easily into byte arrays representing the amplitude. I'm interested in calculating the average of the absolute values of a byte array. For e.g see this listener related to Visualizer in Android.
I know I can cast it to an integer and take the absolute value of that, but I would still be interested why is this not predefined.
The operations in java.lang.Math are in line with all other arithmetic operations in Java. Integer operations always work in either, 64 bit long or 32 bit int.
As stated in JLS, §4.2.2. Integer Operations
If an integer operator other than a shift operator has at least one operand of type long, then the operation is carried out using 64-bit precision, and the result of the numerical operator is of type long. If the other operand is not long, it is first widened (§5.1.5) to type long by numeric promotion (§5.6).
Otherwise, the operation is carried out using 32-bit precision, and the result of the numerical operator is of type int. If either operand is not an int, it is first widened to type int by numeric promotion.
In other words, not even the following, equivalent to abs, would compile:
byte a = 42, absA = a < 0? -a: a;
as the numeric operation -a will promote a to int before negating.
It’s important that a cast of the result to byte would not be a lossless operation here. The byte datatype has a value range from -128 to +127, so if the value is -128, its absolute value +128 is outside the byte value range and a cast to byte would cause an overflow to -127.
Therefore, to have a correct and efficient calculation, you should do as always in Java when it comes to byte, short, or char calculations, calculate everything using int and only cast the final result back to your data type. When you want to calculate the average, you have to calculate the sum using int anyway (or even long if you have more than 16777215 array elements).
byte[] array // e.g. test case: = { 1, -1, -128, 127 };
int sum = 0;
for(byte b: array) sum += Math.abs(b);
int average = sum/array.length;
// if you really need a byte result
byte byteAverage = average == 128? 127: (byte)average;
I don’t know about Kotlin, but in Java, the automatic promotion to int also works if the operand is of type Byte, so you don’t need to “cast it to an integer” to call Math.abs(int). You only have to deal with the fact that the result will be an int, as with all arithmetic operations on byte, short, char, or their wrapper types.
In java byte is signed between -128 and 127, corresponding as (unsigned) int: 0xFF & b between 128 .. 255, and 0 .. 127.
Math.abs is irrelevant here as probably unsigned byte values are assumed.
int[] bytesToInt(byte[] bs) {
int[] is = new int[bs.length];
Arrays.fill(is, i -> bs[i] & 0xFF);
return is;
}
byte byteAbs(byte b) {
return b >= 0? b : b == -128? 127 : -b;
}
byteAbs - given for completeness - reduces the range to 7 bits, and has the artefact that -128 maps to 127, as there is no 128.
How a byte value b when uses as int in the Integer.toBinaryString() have a binary value much more than byte type can contain? I thought s1 should be in range [0, 11111111]. And how this bitwise operator (b & 0B11111111) changes the situation? It seems that it changes nothing because 0 & 1 = 0 and 1 & 1 = 1
public class Test
{
public static void main(String[] args)
{
byte b = (byte) -115;
String s1 = Integer.toBinaryString(b);
String s2 = Integer.toBinaryString(b & 0B11111111);
System.out.println(s1); // 11111111111111111111111110001101
System.out.println(s2); // 10001101
}
}
Integer.toBinaryString accepts an int, so you are actually converting the int -115 to a binary string. As far as Integer.toBinaryString is concerned, the number you passed to it is always an int. The fact that you are able to pass a byte is because there is a conversion from byte to int in an invocation context. The byte is converted to int first, then passed to Integer.toBinaryString.
What's -115 represented in 32 bit two's complement (representation of an int)? Well,
1111 1111 1111 1111 1111 1111 1000 1101
That is the binary string you got.
The & 0B11111111 here actually does something. The & operator causes the byte to undergo numeric promotion, converting it to an int. Now we have -115 as a 32-bit int, with all those extra leading 1s that we don't want. The bit mask then gets applied, resulting in the int 141 (1000 1101). We then convert this int to a binary string.
For a byte, -115 dec is 10001101 bin. If you promote that byte to an int with the same value (which is what happens when you call Integer.toBinaryString(b)), -115 dec is 11111111111111111111111110001101 bin.
Whereas if you take your int -115 and & it with 0B11111111, you get the last eight bits of 11111111111111111111111110001101, which is 10001101 bin, 141 dec.
For a byte 10001101 bin is -115, because the largest bit is negative.
For an int 10001101 bin is 141 dec, and the largest bit isn't set because you're only using the smallest 8 bits of a 32 bit integer.
I understand that the operands are automatically converted to int and we need to cast the expression to byte again. And for byte conversion the 24 bits are truncated and only 8 bits are evaluated. But I am not able to understand this output -56. The final value of e is 200 and converting it in binary gives 11001000. How is the output -56?
public class ByteIntAutomaticPromotionInExpressions {
public static void main(String[] args) {
byte e = 50;
e = (byte)(e *2);
System.out.println(e);
e *= 2;
System.out.println(e);
}
}
OUTPUT:
100
-56
As you can see here:
byte: The byte data type is an 8-bit signed two's complement integer. It has a minimum value of -128 and a maximum value of 127 (inclusive).
https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
If the data type was unsigned, 11001000 would be 200 in decimal.
But since it is signed, you treat it like a negative binary number which is -(inverted bits +1) => - (0110111 + 1) = -(0111000) = -56
https://www.allaboutcircuits.com/textbook/digital/chpt-2/negative-binary-numbers/
8 bits, 2^8=256 in this case 200-256 = -56
When i execute this code it gives value of s as -7616.
Why so? Is this because loss of data while converting it to short from int or something else?
public static void main(String[] args) {
// TODO code application logic here
short s=0;
int x=123456;
int i=8;
s +=x;
System.out.println(s);
}
You are simply overflowing the maximum value of a short :
short: The short data type is a 16-bit signed two's complement
integer. It has a minimum value of -32,768 and a maximum value of
32,767 (inclusive). As with byte, the same guidelines apply: you can
use a short to save memory in large arrays, in situations where the
memory savings actually matters.
What happens when there is such overflow is equivalent to this algorithm :
/** Returns an integer which is equal to the short obtained by the ((short) n) conversion */
public static int int2short(int n) {
int sign = n > 0 ? 1 : -1;
int increment = sign * (Short.MAX_VALUE - Short.MIN_VALUE + 1);
for ( ; n > Short.MAX_VALUE || n < Short.MIN_VALUE ; n -= increment);
return n;
}
Good question! It made me think about things I haven't thought about in a long while and I had to brush up on a couple of concepts. Thanks for helping me knock the rust off my brain.
For me this type of question is best visualized in binary (for reasons that will quickly become apparent):
Your original number (forgive the leading zeroes; I like groups of 4):
0001 1110 0010 0100 0000
A short, however, is a 16-bit signed two's complement integer according to the Java Language Specification (JLS) section 4.2. Assigning the integer value 123456 to a short is known as a "narrowing primitive conversion" which is covered in JLS 5.1.3. Specifically, a "narrowing conversion of a signed integer to an integral type T simply discards all but the n lowest order bits, where n is the number of bits used to represent type T."
Discarding all but the lowest 16 bits leaves us with:
1110 0010 0100 0000
In an unsigned integer this value is 57,290, however the short integer is a signed two's complement integer. The 1 in the leftmost digit indicates a negative number; to get the value of the number you must invert the bits and add 1:
Original:
1110 0010 0100 0000
Invert the bits:
0001 1101 1011 1111
Add 1:
0001 1101 1100 0000
Convert that to decimal and add the negative sign to get -7,616.
Thanks again for asking the question. It's okay to not know something so keep asking and learning. I had fun answering...I like diving into the JLS, crazy, I know!
Incrementing a short beyond its maximum value is called overflow. When overflow occurs, the value becomes the minimum value of the type, and start counting up again.
So here's how you got -7616 from trying to store 0+123456 in a short:
0 --> 32767
-32768 --> 32767
-32768 --> -7616
In other words,
32768+ 32768+ 32768+ (32768 -7616) = 123456
The compound assignment operator += (all of them really) cast their result
A compound assignment expression of the form E1 op= E2 is equivalent
to E1 = (T) ((E1) op (E2)), where T is the type of E1, except that E1
is evaluated only once.
So
s += x;
becomes
s = (short) (s + x);
Now, because s is a short and x is an int, binary numeric promotion is performed on the short value before the addition is applied. The short value is converted to an int (which is not a problem).
s = (short) (8 + 123456)
s = (short) (123464)
The cast applies a narrowing primitive conversion which
[...] may lose information about the
overall magnitude of a numeric value and may also lose precision and
range.
That's what happens
s = -7608
I write the following sample code:
public static void main(String[] args) throws Exception
{
byte number_1 = 127;
byte number_2 = (byte) 128;
System.out.println("number_1 = " + number_1);
System.out.println("number_2 = " + number_2);
}
I get the following result in output:
number_1 = 127
number_2 = -128
I know range of a byte data type( -128 to 127).
Is my sample is correct? What happened? Is there a two's complement operation? I don't understand this behavior.
Because one byte can hold upto -128 to 127 only, This is expected behavior of overflow
Check with this loop
for(int index = 0 ; index < 258 ; index ++ ){
System.out.println((byte)index);
}
Also See
Endless for loop
Nice comic illustration
This is because of byte range . A byte can store values from -128 to 127 only.
You're seeing the effect of a narrowing primitive conversion: casting the integer literal 128 to a byte results all all but the last byte being thrown out. The last byte of an integer has value 10000000, which when interpreted in two's complement as a one-byte value comes out to -128.
In contrast, the same value interpreted as a four-byte value filled with zeroes on the left, i.e. 00000000 00000000 00000000 10000000, equals 128.
From the official documentation on primitive datatypes :
byte: The byte data type is an 8-bit signed two's complement integer.
It has a minimum value of -128 and a maximum value of 127 (inclusive).
To respond your question : How to Set greater than 127 int byte variable in java ?
The best way to represent an unsigned byte is to use a signed integer, because the Java VM represents bytes as 32 bits, you're not saving anything by using a byte.
Because one byte can hold upto -128 to 127 only, when you transform a int which is
more than 127 or less than -127, the java compiler makes the change automatically.
In fact, the following statement
byte number_2 = (byte) 128;
has been changed to
byte number_2 = (byte) -128;
Once you check out the bytecode using javap, you will find it.