lossy conversion from int to byte? [duplicate] - java

This question already has an answer here:
What does "possible lossy conversion" mean and how do I fix it?
(1 answer)
Closed 2 years ago.
my simple encryption algorithim i am doing as an exercise is giving me a possible lossy conversion from int to byte? but i dont know why it is giving me this error any ideas?
public class Rot13Crypt extends CryptStream
{
public Rot13Crypt(StreamPair theStreams)
{
super(theStreams);
}
protected byte [] cryptData(byte [] data, int len)
{
byte [] cryptedByte = new byte[len];
for(int i = 0; i < len; i++)
{
cryptedByte[i] = (data[i] + 13) % 256;
}
return cryptedByte;
}
protected byte [] decryptData(byte [] data, int len)
{
byte [] decryptedByte = new byte[len];
for(int i = 0; i < len; i++)
{
decryptedByte[i] = (data[i] * 256) - 13;
}
return decryptedByte;
}
}

It is giving that error message because you are attempting to assign the value of an int-valued expression to a byte. That is a potentially lossy operation: intuitively, you cannot put all possible int values into a byte.
The Java language requires you to use a (byte) cast when you assign an int-valued expression to a byte.
There are two places where you are doing this:
cryptedByte[i] = (data[i] + 13) % 256;
decryptedByte[i] = (data[i] * 256) - 13;
In the first one, the value of the expression is in the range 0 to 255 ... but Java byte values are in the range -128 to +127.
In the second one, the expression values potentially have values in the range (-128 * 256) - 13 to `(+127 * 256) - 13. That clearly won't fit.
But that is actually moot. The Java does not allow variants of the above code even if you (a smart human being) can prove that the range of the expression would "fit" into a byte. The JLS forbids this. (A Java compiler not required to be a general theorem prover!!)
The only situation where an int-valued expression can be assigned to a byte without a type-cast is when the expression is a compile-time constant expression AND the actual value of the expression is in the range of byte.
If you are interested, this is specified in JLS 15.2 Assignment Contexts which states:
In addition, if the expression is a constant expression (ยง15.28) of type byte, short, char, or int:
A narrowing primitive conversion may be used if the type of the variable is byte, short, or char, and the value of the constant expression is representable in the type of the variable.
(You need to chase down what the spec means by "constant expression" and "narrowing primitive conversion". I'll leave that for interested people to do for themselves.)
so would i add a (byte) cast in front of the 13 and 256?
Nope. You need to cast the entire expression; e.g.
cryptedByte[i] = (byte) ((data[i] + 13) % 256);

In Java, the int type is coded using 32 bits and the byte type using 8 bits. If you convert an int to a byte, you may lose information if your original value is greater than 127 or less than -128.
An integer literal is by default an int and operations on int return int. So the result of data[i] + 13 is an int; the same for (data[i] + 13) % 256. When you try to store that result into a byte (here cryptedByte[i]), Java warns you about the "possible lossy conversion".

A byte has 2^8 = 256 unique states and so the largest range it can represent is -128 to 127.
so assuming that in
decryptedByte[i] = (data[i] * 256) - 13;
the data[i] value is greater than 1 will cause the value to overflow the max value of a byte

You have problems here
cryptedByte[i] = (data[i] + 13) % 256;
and here
decryptedByte[i] = (data[i] * 256) - 13;
256 and 13 are literals of type int. When you perform operations with them and with a type that occupies less memory (in this example, byte), type promotion occurs: data[i] is implicitly converted (promoted) to type int. Thus, both these expressions evaluate to int. And you know what happens when you convert an int to a byte, right? You can lose data. That is because a byte is limited to less memory than int.

You just need to do a cast when assigning each byte when it's inside a variable, so java can assure all are between -127 and 127 which is the ascii range. The folowing should work:
public class Rot13Crypt extends CryptStream
{
public Rot13Crypt(StreamPair theStreams)
{
super(theStreams);
}
protected byte [] cryptData(byte [] data, int len)
{
byte [] cryptedByte = new byte[len];
for(int i = 0; i < len; i++)
{
cryptedByte[i] = (byte) (data[i] + 13) % 256;
}
return cryptedByte;
}
protected byte [] decryptData(byte [] data, int len)
{
byte [] decryptedByte = new byte[len];
for(int i = 0; i < len; i++)
{
decryptedByte[i] = (byte) (data[i] * 256) - 13;
}
return decryptedByte;
}
}

Bytes have a size of 8 bits and ints a size of 32 bits. So some information may be lost by doing this conversion!

byte data type is an 8-bit signed two's complement integer.
int data type is a 32-bit signed two's complement integer.
Can't fit an integer into a byte. Hence, the loss in precision.
I hope it's clear.
A sample reference is here

Related

Subtraction/difference between bytes in Java

I'm trying to subtract one byte from another while making sure no overflow happens, but get unexpected results.
My case is that I have an byte black and white image, of which I want to subtract the background. Hence I need to work with bytes and prevent overflows from happening. I have some difficulty presumably with the signedness of the bytes when subtracting the background image from the other image. Data2 is the background array, and data1 is the other image.
In the following code, I expect data2 array to be subtracted from data1 array. However I get low values when I am sure there should be high ones.
for (int i = 0; i < data1.length; i++) {
if (data1[i] > data2[i]) {
dest[i] = (byte) (data1[i] - data2[i]);
} else {
dest[i] = 0;
}
}
I figured I should make sure data2 byte isn't negative and being added to data1.
So I came to:
for (int i = 0; i < data1.length; i++) {
if (data1[i] > data2[i]) {
dest[i] = (byte) ((int)data1[i] & 0xff - (int)data2[i] & 0xff - 128);
} else {
dest[i] = 0;
}
}
However this also doesn't give the right results.
My thoughts on this currently are:
(byte) ((int)data1[i] & 0xff - (int)data2[i] & 0xff - 128);
(int) cast: make sure bytes are cast to integer.
&0xff: make value unsigned.
- subtraction smaller value from bigger value.
- 128: subtract 128 to make signed again.
(byte): cast back to byte.
I hope I'm doing something stupidly wrong here, or my problem resides somewhere else of which I can't figure out where.
Edit
I seem to have figured out a part of the issue:
data1[i] > data2[i] is handled wrong (in my case) when the bytes are signed. Instead:
if ((data1[i] & 0xff) > (data2[i] & 0xff)) {
seems to produce the right results, instead of the previous comparison.
The point here is that your bytes come from an API that uses 8 bits to encode the light of the pixel, so they range 0; 0xFF. However Java bytes are -128; 127, so any bit pattern after 0x7F will be interpreted as a negative number. For example the bits in 0xF0 are 128 if the byte is unsigned and -16 if interpreted as a signed byte:
System.out.println(0xFF > 0); // true
System.out.println((byte) 0xFF > 0); // false
So when comparing your unsigned bytes you want to promote pixels to int with Byte.toUnsignedInt(byteVal) (or ((int) byteVal) & 0xFF on Java 7).
Always remember Java bytes are signed. If data1[i] is 127, and data2[i] is -128, then data1[i] > data2[i], but data1[i] - data2[i] does not fit into a signed byte; that result is 255.
If you treat the bytes as unsigned, that's fine. That more-or-less means printing them out after using & 0xFF, and such. That will work just fine; it will give the right results if you treat them as unsigned correctly.
To ensure that you only substract non negative bytes from from bytes that are greater you should use :
for (int i = 0; i < data1.length; i++) {
if (data1[i] > data2[i] && data2[i]>=0 ) {
dest[i] = (byte) (data1[i] - data2[i]);
} else {
dest[i] = 0;
}
}
Your second code does not work because the & operator promotes the values to type int.
Consider the case where data1=127 (0x7F) and data2=-128 (0x80).
data1 & 0xff is type int and has value 127 (0x0000007F)
data2 & 0xff is type int and has value 128 (0x00000080)
data1[i] & 0xff - data2[i] & 0xff is type int and has value -1 (0xFFFFFFFF)
(byte)(data1[i] & 0xff - data2[i] & 0xff) is type byte and has value -1 (0xFF)
So you still have gotten an overflow
For some reason, comparing bytes is handled weirdly. If I convert the bytes to unsigned ints in the comparison, the comparison works correctly and my results are as I expected them.
I can then subtract the bytes as if they were unsigned as Louis Wasserman pointed out, which was new to me.
for (int i = 0; i < data1.length; i++) {
if ((data1[i] & 0xff) > (data2[i] & 0xff)) {
dest[i] = (byte)(data1[i] - data2[i]);
} else {
dest[i] = 0;
}
}

conversion datatype assign in java

I don't understand why i should use {b=(byte) i} or {i =(int) d} this to conversion . Please can say me cordially why it happens in conversion. My sample code below.
public class Conversion {
public static void main(String[] args) {
byte b;
int i = 257;
double d = 323.142;
System.out.println("\n Conversion int to byt");
b = (byte) i;
System.out.println("i and b "+i+ " after "+b);
System.out.println("\n Conversion int to byt");
i = (int) d;
System.out.println("d and i "+d+ " after "+i);
}
}
byte have range from -128 to 127 if you exceeds its limit you have to explicitly cast it to int
the same way int have range and if it exceeds from its range you have to explicitly cast it
if we have value of byte in between the range of -128 to 127 there is no requirement of explicitly cast it to int
for more help i have follow link which will guide you
Oracle description about primitive data type
Why is the range of bytes -128 to 127 in Java?
Chapter 5. Conversions and Promotions
You are doing "Widening Primitive Conversion" and "Narrowing Primitive Conversion".
https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.2
https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.3

Binary Representation to a new byte

I'm trying to create a new byte knowing a certain amount of bits
char prostie1 = theRepChars[j-3];
char prostie2 = theRepChars[j-2];
char prostie3 = theRepChars[j-1];
char prostie4 = theRepChars[j];
String prostiaMare = prostie4 + prostie3 + prostie2 + prostie1 + "";
Byte theChar = new Byte(prostiaMare);
When i do this I get a NumberFormatException value 196.
I have no idea what might be my problem
--EDIT--
Ok I think I might have to give some more details since I wasn't very clear. I'm trying to do an Uuencode algorithm and by following the logic of the algorithm I should stop my byte having a value bigger than 194. Here is a bunch of my code.
if(my_chars.length % 3 == 0)
{
for(int x = 0; x < my_chars.length; x++)
{
if((x+1) % 3 == 0)
{
char first = my_chars[x-2];
char second = my_chars[x-1];
char third = my_chars[x];
int n = (((first << 8) | second) << 8) | third;
String theRep = Integer.toBinaryString(n);
while(theRep.length() < 24 - 1)
{
theRep = 0 + theRep;
}
//0 padded theRep
for(int j = 0; j < theRepChars.length; j++)
{
if((j+1) % 4 == 0)
{
char prostie1 = theRepChars[j-3];
char prostie2 = theRepChars[j-2];
char prostie3 = theRepChars[j-1];
char prostie4 = theRepChars[j];
String prostiaMare = prostie4 + prostie3 + prostie2 + prostie1 + "";
System.out.println(prostiaMare);
}
}
}
}
}
And trying to create a new byte with the value that prostiaMare has gives me the numberFormatException. I'm not sure if I have not followed the algorithm right ( http://www.herongyang.com/encoding/UUEncode-Algorithm.html )
196 is outside the range of byte, a signed value. Bytes can range from -128 to 127.
I'm not sure why you're casting to String. If you just want a byte with bits equivalent those of the sum of the four chars, cast directly to byte:
(byte) (prostie4 + prostie3 + prostie2 + prostie1)
If you intended to construct a String from the four chars, you are not currently doing that. Use:
"" + prostie4 + prostie3 + prostie2 + prostie1
and, if the result is in the range of a byte, you can create a byte as you have been.
Bytes are signed in Java. Which means a byte, which is 8 bits long, has a minimum value of -2^7 (-128) and a max value of 2^7 - 1 (127). Java has no unsigned primitive types apart from char (unsigned, 16bit).
Therefore 196 is unparseable --> NumberFormatException.
You don't have much to work around this except to read into a larger type and do & 0xff to obtain the byte:
final int i = Integer.parseInt(theString);
final byte b = (byte) (i & 0xff);
Or do yourself a favour and use Guava, which has UnsignedBytes:
final byte b = UnsignedBytes.parseUnsignedByte(theString);
But it appears that you want to do comparisons anyway; so just use a larger type than byte. And no, this won't waste memory: don't forget about alignment.
As mentioned in the docs
An exception of type NumberFormatException is thrown if any of the following situations occurs:
The first argument is null or is a string of length zero.
The radix is either smaller than Character.MIN_RADIX or larger than Character.MAX_RADIX.
Any character of the string is not a digit of the specified radix, except that the first - character may be a minus sign '-' ('\u002D') provided that the string is longer than length 1.
The value represented by the string is not a value of type byte.
In your case its the last case since 196 cant be represented as byte..The valid range is -128 to 127

some thing wrong with array index in byte[]

I have string like this
String text = "f001050000000000003d61c1c1df400200c0000009181600ef014000003f20"
I converted it to bytes to loop through it as bytes
byte[] bytes = new BigInteger(text,16).toByteArray();
for (int i = 0; i < bytes.length; i++)
{
System.out.print(String.format("%02x ", bytes[i]));
}
But when I print array values it adds byte 00 at the beginning of the actual string!
It should start with f0 but it starts with 00!
When I start index with 1 this 00 disappear.
From where this 00 come!?
JavaDoc of BigInteger#toByteArray() states:
Returns a byte array containing the two's-complement
representation of this BigInteger. The byte array will be in big-endian byte-order: the most significant byte is in the zeroth element. The array will contain the minimum number of bytes required to represent this BigInteger, including at least one sign bit, which is (ceil((this.bitLength() + 1)/8)).[...]
As you have a positive number, the first bit will be zero in two's complement.
I think BigInteger is no good for this task. You need to parse your text yourself, it's not difficult
byte[] bytes = new byte[text.length() / 2];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) ((Character.digit(text.charAt(i * 2), 16) << 4) + Character.digit(text.charAt(i * 2 + 1), 16));
}
not that it also converts "0000f0..." correctly but BigInteger would truncate leading zeroes (normalize) because for BigInteger it is just a number

Java convert long to 4 bytes

How do I convert long to 4 bytes? I am receiving some output from a C program and it uses unsigned long. I need to read this output and convert this to 4 bytes.
However, java uses signed long which is 64 bits. Is there any way to do this conversion?
To read 4 bytes as an unsigned 32-bit value, assuming it is little endian, the simplest thing to do is to use ByteBuffer
byte[] bytes = { 1,2,3,4 };
long l = ByteBuffer.wrap(bytes)
.order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xFFFFFFFFL;
While l can be an signed 64-bit value it will only be between 0 and 2^^32-1 which is the range of a unsigned 32-bit value.
You can use the java.nio.ByteBuffer. It can parse the long, and it does the byte ordering for you.
You can code a loop where you divide the "long" by 256, take the rest, then you have the "Least Significant Byte" ...
(depending on whether you want little-endian or big-endian you can loop forwards or backwards)
long l = (3* 256 * 256 * 256 + 1 * 256 *256 + 4 * 256 + 8);
private byte[] convertLongToByteArray(long l) {
byte[] b = new byte[4];
if(java.nio.ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN){
for (int i=0; i<4; i++) {
b[i] = (byte)(l % 256) ;
l = l / 256;
}
}else{
for (int i=3; i>=0; i--) {
b[i] = (byte)(l % 256) ;
l = l / 256;
}
}
return b;
}

Categories

Resources