我们可以在Java中制作无符号字节吗

Posted

技术标签:

【中文标题】我们可以在Java中制作无符号字节吗【英文标题】:Can we make unsigned byte in Java 【发布时间】:2011-05-15 02:01:03 【问题描述】:

我正在尝试将有符号字节转换为无符号字节。问题是我收到的数据是无符号的,Java 不支持无符号字节,所以当它读取数据时,它会将其视为有符号的。

我尝试通过从 *** 获得的以下解决方案对其进行转换。

public static int unsignedToBytes(byte a)

    int b = a & 0xFF;
    return b;

但是当它再次转换为字节时,我得到了相同的签名数据。我正在尝试将此数据用作 Java 函数的参数,该函数仅接受一个字节作为参数,因此我不能使用任何其他数据类型。我该如何解决这个问题?

【问题讨论】:

番石榴:UnsignedBytes.toint(字节值) java.lang.Byte.toUnsignedInt(字节值); 【参考方案1】:

在 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"

【讨论】:

对于许多操作它没有区别,但是对于某些操作它确实如此。无论哪种方式,您都可以使用无符号字节,或使用无符号字符。 访问具有潜在负数的数组并非无关紧要。 @Stefan - 我的意思是与它们在网络上的表示方式无关。 这与问题无关。因为他提到他需要将它传递给一个只接受字节参数的函数,所以不管天气如何,我们将它解释为独角兽的字节表示。 Java 将始终将其视为有符号数,例如,当此函数将参数用作索引时,这可能会出现问题。但公平地说,我也对其他前 2 个答案投了反对票,因为他们也没有回答这个问题。 @Stefan +1 为您服务。如果您使用字节访问 256 个元素的数组,则绝对相关。这是一个很好的例子来说明为什么每个人都应该在转向 Java 或 C# 之前开始学习 C 和 C++【参考方案2】:

我不确定我是否理解你的问题。

我刚刚尝试过,对于字节 -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.

【讨论】:

byte b = (byte)unsignedToBytes((byte) -12); 现在尝试打印 b 您为什么接受这个作为正确答案?它所做的一切与您在问题中提到的方法完全相同 - 将字节转换为无符号整数。 有时有符号的值很重要,有时是无符号的,所以这可能是他接受这个答案的原因。 (byte)(b & 0xff) 没有任何意义,但 (byte)(Math.min((b & 0xff)*2, 255)) 有意义,例如在计算机图形学中它只会使像素表示为字节亮两倍。 :-) 也可以叫byteToUnsigned【参考方案3】:

在 Java 中使用无符号字节的完整指南:

Unsigned byte in Java

(此答案的来源。)


Java 语言不提供类似unsigned 关键字的内容。根据语言规范,byte 表示介于 -128 - 127 之间的值。例如,如果将 byte 强制转换为 int,Java 会将第一位解释为符号并使用 sign extension。

话虽如此,没有什么可以阻止您将 byte 简单地视为 8 位并将这些位解释为 0 到 255 之间的值。请记住,您无法将自己的解释强加于其他人的方法.如果一个方法接受 byte,那么除非另有明确说明,否则该方法接受介于 -128 和 127 之间的值。

为了您的方便,这里有几个有用的转换/操作:

与 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-补码表示“正好适用于”加法、减法和乘法:

// 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));

【讨论】:

【参考方案4】:

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

int anUnsignedByte = (int) aSignedByte & 0xff;

【讨论】:

是否需要强制转换为 int? 它可以是隐式转换,但无论哪种方式都有转换。那个演员确实签署了延期。这是一个问题。如果您进行显式转换,您至少可以看到这种情况正在发生。【参考方案5】:

我认为其他答案已经涵盖了内存表示,您如何处理这些取决于您计划如何使用它的上下文。我将添加Java 8 added some support for dealing with unsigned types。在这种情况下,您可以使用Byte.toUnsignedInt

int unsignedInt = Byte.toUnsignedInt(myByte);

【讨论】:

【参考方案6】:

附注,如果你想打印出来,你可以说

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

【讨论】:

为什么这么复杂? println(b &amp; 0xff)就够了【参考方案7】:

您还可以:

public static int unsignedToBytes(byte a)

    return (int) ( ( a << 24) >>> 24);
    

说明:

假设a = (byte) 133;

在内存中存储为:“1000 0101”(0x85 十六进制)

所以它的表示转换为 unsigned=133, signed=-123(作为 2 的补码)

一个

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

“10000101 00000000 00000000 00000000”(或十六进制的“0x85000000”)

那么我们有

(a >>> 24

它再次向右移动 24 位,但用前导零填充。所以结果是:

“00000000 00000000 00000000 10000101”(或十六进制的“0x00000085”)

那是等于 133 的无符号表示。

如果您尝试投射a = (int) a; 那么会发生什么是它保留字节的 2 的补码表示并将其存储为 int 也作为 2 的补码:

(int) "10000101" ---> "11111111 11111111 11111111 10000101"

翻译为:-123

【讨论】:

2019年,这是不必要的。只需使用java.lang.Byte.toUnsignedInt(byte value)。如果您还没有使用 Java 8,请尽快升级。 Java 7 及更早版本的生命周期结束。 我认为这种老式的替代方案仍然具有启发性。对于那些想知道 '>>' 和 '>>>' 之间区别的人,这里是documentation:带符号的左移运算符“>" 向右移动一个位模式。无符号右移运算符“>>>”将零移到最左边的位置,而“>>”之后的最左边的位置取决于符号扩展。【参考方案8】:

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

【讨论】:

看看其他人的回答,你认为你的回答是最好的/有帮助的吗?稍微描述一下,添加到cmets中 这不是稀有,因为你没有遇到过。尝试实现一个协议,你会遇到一百万次。令人讨厌的是,我遇到的绝大多数用例都处理字节,你想处理无符号字节(因为它们是字节,而不是数字)。疯狂的是,任何按位运算都会将其转换为 int,这意味着任何“负”值在扩展时都将是完全不同的值。是的,您可以通过始终屏蔽来绕过它,但这是浪费时间和处理器,并且如果您忘记了会导致非常模糊的错误。 我同意 Thor84no:字节不是数字,不应该有符号。另一方面,由于它们不是数字,我们甚至不应该拥有/使用 + 和 - 运算符。仅使用按位运算符可以正常工作,另一方面,移位运算符不能按预期工作,并且确实 java 将移位字节提升为 int。 @VlastimilOvčáčík 在这种情况下这简直是不可能的,这是令人不安的事情。你要么在任何你需要的地方重复x &amp; 0xFF,要么在任何地方重复behaveLikeAnUnsignedByte(x)之类的东西。这对于您使用需要无符号的字节值或字节数组的每个地方都是必需的,没有可以想象的避免这种重复的方法。您不能编写一个协议的实现,该协议仅通过对字节变量的单个引用来读取和写入字节值。你的简单化观点或许可以解释为什么他们从不关心修复它。【参考方案9】:

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

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

【讨论】:

【参考方案10】:

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

如果您有一个系统函数需要将无符号字节传递给它,您可以传递一个有符号字节,因为它会自动将其视为无符号字节。

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

但是,您不太可能遇到此问题,因为系统函数隐藏在类后面以实现跨平台兼容性,尽管一些 java.io 读取方法将 unsighed 字节作为 int 返回。

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

【讨论】:

不存在有符号或无符号字节。 您在示例中究竟是如何写入和读取字节的?【参考方案11】:

我正在尝试将此数据用作 Java 函数的参数,该函数只接受一个字节作为参数

这与接受整数的函数没有本质区别,您希望将大于 2^32-1 的值传递给该函数。

这听起来取决于函数是如何定义和记录的;我可以看到三种可能性:

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

    它可能会记录该参数的值必须大于(或可能等于)零,在这种情况下,您正在滥用函数(传递超出范围的参数),期望它这样做超过它的设计目的。借助一定程度的调试支持,您可能希望该函数抛出异常或使断言失败。

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

【讨论】:

嗯,我想我刚刚发布了一个回复,旨在解决另一个关于字节符号的问题,但我想它在这里也有点相关......【参考方案12】:

在想知道 netty ByteBuf writeInt 和 readUnsignedInt 方法的明显不对称性后,我偶然发现了这个页面。

在阅读了有趣且具有教育意义的答案后,我仍然想知道您说的是什么函数:

我正在尝试将此数据用作 Java 函数的参数 只接受一个字节作为参数。

这么多年后它值多少钱,这是我的 50 美分:

让我们假设您调用的方法正在使用微量金额更新一些余额,并且它的行为符合一些明确定义的要求。即,它被认为对其预期行为有正确的实现:

long processMicroPayment(byte amount) 
    this.balance += amount;
    return balance;     

基本上,如果您提供正数,它将被添加到余额中,而负数将有效地从余额中减去。现在因为它接受一个字节作为其参数,所以隐含的假设是它在功能上只接受 -128 和 +127 之间的数量。因此,如果您想使用此方法将 130 添加到余额中,它根本不会产生您想要的结果,因为在此方法的实现中无法表示高于 127 的金额。所以传递它130不会导致你想要的 行为。请注意,该方法无法实现(例如)AmountOutOfBoundsException,因为 130 将被“解释”为仍遵守方法约定的负值。

所以我有以下问题:

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

【讨论】:

【参考方案13】:

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

int myInt = 144;

byte myByte = (byte) myInt;

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

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

输出:

myChar : 90

欲了解更多信息,请查看,How to display a hex/byte value in Java

【讨论】:

不需要自己定义。 java.lang.Byte.toUnsignedInt(byte value); 存在于此。【参考方案14】:

是和不是。我一直在研究这个问题。 就像我理解的那样:

事实上,java 已经签署了整数 -128 到 127.. 可以在 java 中显示一个未签名的:

public static int toUnsignedInt(byte x) 
    return ((int) x) & 0xff;

例如,如果您将 -12 有符号数字添加为无符号数字,您将得到 244。但是您可以再次使用该数字符号,它必须移回有符号数字,它将再次变为 -12。

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

干杯..

【讨论】:

不需要自己定义。 java.lang.Byte.toUnsignedInt(byte value); 存在于此。【参考方案15】:

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

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

通常您可以将一个字节用作无符号字节,只需简单翻译或不翻译。这一切都取决于它是如何使用的。你需要澄清你打算用它做什么。

【讨论】:

【参考方案16】:

根据 Java 的限制,在当前数据类型格式中几乎不可能使用无符号字节。您可以使用其他语言的其他库来实现您正在实现的功能,然后您可以使用JNI 调用它们。

【讨论】:

我认为他不想将其存储为有符号字节。他将其作为有符号字节接收,并希望将其存储为 int,这是完全有效的。他的问题是,无论他从哪里获取输入,都将 0 到 255 之间的值表示为一个字节,但 Java 将其解释为二进制补码有符号值,因为 java 不支持有符号字节。【参考方案17】:

如果您想要 Java 中的无符号字节,只需从您感兴趣的数字中减去 256。它会生成带有负值的 two's complement,这是所需的无符号字节数。

例子:

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 brick 进行编程时,您需要使用这种肮脏的技巧。

【讨论】:

你确实知道255的二进制值也是1111 1111,所以不需要减法,对吧? @NickWhite,是的,二进制。但是 java 使用 2 的补码,其中 255 不是 11111111 抱歉,这是错误的。尝试一些实验。 speed_unsigned 中的值已签名。打印出来看看。 (而- 256 在这里一无所获。)

以上是关于我们可以在Java中制作无符号字节吗的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Java 中的无符号字节中获取浮点数?

C中的无符号整数在java中的处理

如何从 Java 中的 BigInteger 获取无符号字节数组?

整数的无符号编码和有符号编码

如何对实际上是 int[] 的无符号字节数组进行 base64 编码

将 4 个字节转换为无符号 32 位整数并将其存储在 long