我们能在Java中创建无符号字节吗

我试图转换一个无符号的有符号字节。问题是我接收的数据是无符号的,Java不支持无符号字节,所以当它读取数据时,它将其视为有符号的。

我尝试通过下面的解决方案转换它,我从Stack Overflow。

public static int unsignedToBytes(byte a)
{
int b = a & 0xFF;
return b;
}

但是当它再次以字节为单位转换时,我得到了相同的带符号数据。我试图使用此数据作为参数的Java函数,只接受一个字节作为参数,所以我不能使用任何其他数据类型。我该如何解决这个问题?

308479 次浏览

如果你有一个函数必须传递一个有符号字节,如果你传递一个无符号字节,你期望它做什么?

为什么不能使用其他数据类型?

通常情况下,您可以使用一个字节作为一个无符号字节简单或不翻译。这完全取决于如何使用。你需要澄清你打算用它做什么。

在Java中,原语是有符号的,这与它们在内存/传输中的表示方式无关——一个字节只有8位,是否将其解释为有符号范围取决于您。没有神奇的旗帜说“这是有符号的”或“这是没有符号的”。

由于原语是有符号的,Java编译器将阻止您为字节分配大于+127的值(或小于-128的值)。然而,没有什么可以阻止你向下转换一个int型(或short型)来实现这一点:

int i = 200; // 0000 0000 0000 0000 0000 0000 1100 1000 (200)
byte b = (byte) 200; // 1100 1000 (-56 by Java specification, 200 by convention)


/*
* Will print a negative int -56 because upcasting byte to int does
* so called "sign extension" which yields those bits:
* 1111 1111 1111 1111 1111 1111 1100 1000 (-56)
*
* But you could still choose to interpret this as +200.
*/
System.out.println(b); // "-56"


/*
* Will print a positive int 200 because bitwise AND with 0xFF will
* zero all the 24 most significant bits that:
* a) were added during upcasting to int which took place silently
*    just before evaluating the bitwise AND operator.
*    So the `b & 0xFF` is equivalent with `((int) b) & 0xFF`.
* b) were set to 1s because of "sign extension" during the upcasting
*
* 1111 1111 1111 1111 1111 1111 1100 1000 (the int)
* &
* 0000 0000 0000 0000 0000 0000 1111 1111 (the 0xFF)
* =======================================
* 0000 0000 0000 0000 0000 0000 1100 1000 (200)
*/
System.out.println(b & 0xFF); // "200"


/*
* You would typically do this *within* the method that expected an
* unsigned byte and the advantage is you apply `0xFF` only once
* and than you use the `unsignedByte` variable in all your bitwise
* operations.
*
* You could use any integer type longer than `byte` for the `unsignedByte` variable,
* i.e. `short`, `int`, `long` and even `char`, but during bitwise operations
* it would get casted to `int` anyway.
*/
void printUnsignedByte(byte b) {
int unsignedByte = b & 0xFF;
System.out.println(unsignedByte); // "200"
}

在Java中使用unsigned字节的完整指南:

Java中的无符号字节

(答案来源)


Java语言不提供任何类似unsigned关键字的东西。根据语言规范,byte表示−128 - 127之间的值。例如,如果byte转换为int, Java将把第一位解释为符号并使用符号扩展

也就是说,没有什么可以阻止你将byte简单地视为8位,并将这些位解释为0到255之间的值。只要记住,你不能把你的解释强加给别人的方法。如果一个方法接受byte,则该方法接受−128到127之间的值,除非另有明确说明。

下面是一些有用的转换/操作,方便您使用:

从int到int的转换

// From int to unsigned byte
int i = 200;                    // some value between 0 and 255
byte b = (byte) i;              // 8 bits representing that value

// From unsigned byte to int
byte b = 123;                   // 8 bits representing a value between 0 and 255
int i = b & 0xFF;               // an int representing the same value

(或者,如果你使用的是Java 8+,使用Byte.toUnsignedInt。)

解析/格式化

最好的方法是使用上面的转换:

// Parse an unsigned byte
byte b = (byte) Integer.parseInt("200");

// Print an unsigned byte
System.out.println("Value of my unsigned byte: " + (b & 0xFF));

算法

2补表示法“just work”;加法、减法和乘法:

// two unsigned bytes
byte b1 = (byte) 200;
byte b2 = (byte) 15;


byte sum  = (byte) (b1 + b2);  // 215
byte diff = (byte) (b1 - b2);  // 185
byte prod = (byte) (b2 * b2);  // 225

除法需要手动转换操作数:

byte ratio = (byte) ((b1 & 0xFF) / (b2 & 0xFF));

我不太明白你的问题。

我刚刚尝试了这个,对于字节-12(有符号值),它返回整数244(相当于无符号字节值,但类型为int):

  public static int unsignedToBytes(byte b) {
return b & 0xFF;
}


public static void main(String[] args) {
System.out.println(unsignedToBytes((byte) -12));
}

这是你想做的吗?

Java不允许像c一样将244表示为byte值,要表示在Byte.MAX_VALUE(127)之上的正整数,必须使用不同的整型,如shortintlong

在Java中没有原始无符号字节。通常的做法是将其转换为更大的类型:

int anUnsignedByte = (int) aSignedByte & 0xff;

由于Java中的限制,无符号字节在当前的数据类型格式中几乎是不可能的。你可以为你要实现的东西寻找另一种语言的其他库,然后你可以使用JNI调用它们。

顺便说一句,如果你想打印出来,你可以说

byte b = 255;
System.out.println((b < 0 ? 256 + b : b));

如果您想在Java中使用无符号字节,只需从感兴趣的数字中减去256。它将产生带负值的二进制补码,这是所需的无符号字节数。

例子:

int speed = 255; //Integer with the desired byte value
byte speed_unsigned = (byte)(speed-256);
//This will be represented in two's complement so its binary value will be 1111 1111
//which is the unsigned byte we desire.

当你使用leJOS来编程NXT砖时,你需要使用这种肮脏的技巧。

虽然Java语言中不包含无符号字节看起来很烦人(来自C),但这真的不是什么大问题,因为一个简单的“b &0xFF”操作在实际需要的(罕见)情况下为(有符号)字节b产生无符号值。比特实际上并没有改变——只是解释(这只在例如对值进行一些数学运算时很重要)。

Adamski提供了最好的答案,但它并不完整,所以阅读他的回复,因为它解释了我没有的细节。

如果你有一个系统函数需要传递一个无符号字节给它,你可以传递一个有符号字节,因为它会自动把它当作一个无符号字节。

因此,如果一个系统函数需要四个字节,例如,192 168 0 1作为无符号字节,您可以传递-64 -88 0 1,并且函数仍然可以工作,因为将它们传递给函数的行为将取消它们的符号。

然而,您不太可能遇到这个问题,因为系统函数隐藏在类后面以实现跨平台兼容性,尽管一些java。IO read方法返回一个int类型的未叹号字节。

如果您希望看到这种工作,请尝试将有符号字节写入文件,并将它们作为无符号字节读取回来。

如果你认为你正在寻找这样的东西。

public static char toUnsigned(byte b) {
return (char) (b >= 0 ? b : 256 + b);
}

在Java中没有无符号字节,但是如果你想显示一个字节,你可以这样做,

int myInt = 144;


byte myByte = (byte) myInt;


char myChar = (char) (myByte & 0xFF);


System.out.println("myChar :" + Integer.toHexString(myChar));

输出:

myChar : 90

更多信息,请查看,如何在Java中显示十六进制/字节值

我认为其他答案已经涵盖了内存表示,您如何处理这些取决于您计划如何使用它的上下文。我将添加Java 8增加了一些处理无符号类型的支持。在这种情况下,你可以使用Byte.toUnsignedInt

int unsignedInt = Byte.toUnsignedInt(myByte);

是和不是。我一直在研究这个问题。 就像我明白这个:

事实是java有符号整数-128到127.. 可以用

在java中表示unsigned对象
public static int toUnsignedInt(byte x) {
return ((int) x) & 0xff;
}

如果你把-12有符号数加为无符号数,就得到244。但是你可以再次使用这个数字,它必须被移回符号,它还是-12。

如果你尝试添加244到java字节,你会得到outOfIndexException。

欢呼声……

你还可以:

public static int unsignedToBytes(byte a)
{
return (int) ( ( a << 24) >>> 24);
}

解释:

让我们说a = (byte) 133;

在内存中,它被存储为:"1000 0101"(十六进制中的0x85)

所以它的表示转换为无符号=133, 签署=-123(作为2的补码)

& lt; & lt;24

当向左移动24位时,结果现在是一个4字节的整数,表示为:

"10000101 00000000 00000000 00000000"(或十六进制中的"0x85000000")

然后我们有

(a <<24) > > > 24

它又向右移动了24位,但前导都是0。结果是:

"00000000 00000000 00000000 10000101"(或十六进制中的"0x00000085")

这就是无符号表达式它等于133。

如果你试图强制转换a = (int) a; 然后会发生的是,它保留了字节的2补表示,并将其存储为int,也存储为2的补:

(int)“10000101”——>“11111111 11111111 11111111 10000101”

翻译过来就是-123

我试图使用此数据作为参数的Java函数,只接受一个字节作为参数

这与函数接受一个大于2^32-1的整数并没有本质上的区别。

这听起来似乎取决于函数是如何定义和记录的;我认为有三种可能:

  1. 它可以显式地记录函数将字节视为无符号值,在这种情况下,函数可能应该执行您期望的操作,但似乎实现错误。对于整数情况,函数可能会将形参声明为无符号整数,但对于字节情况则不可能。

  2. 它可能记录了这个参数的值必须大于(或可能等于)零,在这种情况下,您滥用了函数(传递一个超出范围的形参),期望它做的比它设计的要多。使用某种级别的调试支持,您可能希望该函数抛出异常或断言失败。

  3. 文档可能什么也没说,在这种情况下,负参数就是一个负参数,它是否有任何意义取决于函数的功能。如果这是没有意义的,那么也许函数应该定义/文档为(2)。如果这是以一种不明显的方式有意义的(例如,非负值用于索引数组,负值用于从数组的末尾索引,因此-1表示最后一个元素),文档应该说明它的含义,我预计它不是你想要它做的事情。

在好奇netty ByteBuf writeInt和readUnsignedInt方法的明显不对称之后,我碰巧偶然地进入了这个页面。

在阅读了有趣和有教育意义的答案后,我仍然想知道你说的时候调用的是什么函数:

我试图使用这个数据作为参数的Java函数

不管这么多年过去了,我的50美分如下:

让我们假设您正在调用的方法正在用微量更新一些余额,并且它根据一些定义良好的需求集进行操作。也就是说,它被认为对其预期的行为有正确的实现:

long processMicroPayment(byte amount) {
this.balance += amount;
return balance;
}
基本上,如果你提供一个正的数量,它将被添加到余额中,而一个负的数量将有效地从余额中减去。现在因为它接受一个字节作为参数,隐含的假设是它在功能上只接受-128到+127之间的数量。因此,如果您想使用此方法向余额中添加130,它根本不会产生您想要的结果,因为在此方法的实现中没有办法表示高于127的金额。所以通过130不会得到你想要的结果 的行为。注意,该方法没有办法实现(比如说)AmountOutOfBoundsException异常,因为130将被“解释”为一个仍然遵守方法契约的负值

我有以下几个问题:

  • 您是否根据其(隐式或显式)契约使用该方法?
  • 方法是否正确实现?
  • 我还是误解了你的问题吗?