为什么我从 Long.parseUnsignedLong 中获取值,而我不应该这样做



当我这样做的时候

Long.parseUnsignedLong("FBD626CC4961A4FC", 16)

我得到-300009666327239428

这似乎是错误的,因为根据这个答案https://stackoverflow.com/a/2550367/1754020, unsigned long的含义是范围总是正的。

要从这个HEX值中获得正确的数字,我执行

BigInteger value = new BigInteger("FBD626CC4961A4FC", 16);

当我打印value时,它打印正确的值。但如果我用value.longValue()

再次得到相同的-300009666327239428,这是数字太大和溢出吗?

Java 8(在某种程度上)支持无符号长长度,但是,您不能直接打印它们。这样做会得到您所看到的结果。

如果你有一个unsigned long

Long number = Long.parseUnsignedLong("FBD626CC4961A4FC", 16);

可以使用

函数获得正确的字符串表示形式
String numberToPrint = Long.toUnsignedString(number);

如果你现在打印numberToPrint,你得到

18146734407382312188

更确切地说,你的数字仍然是一个常规的符号 long,这就是为什么它显示溢出,如果直接打印。但是,有一些新的静态函数会将该值视为无符号值,例如Long.toUnsignedString(long x)Long.compareUnsigned(long x, long y)

十六进制数"FBD626CC4961A4FC"转换成十进制后正好是18146734407382312188。这个数字确实大于long的最大值,定义为Long.MAX_VALUE,它等于263-1,或者9223372036854775807:

System.out.println(new BigInteger("FBD626CC4961A4FC", 16)); // 18146734407382312188
System.out.println(Long.MAX_VALUE);                         // 9223372036854775807

因此,得到一个负数是正常的。

没有异常,因为这正是在Java 8中添加的那些新的*Unsigned*方法的目的,提供处理无符号长(如compareUnsigneddivideUnsigned)的能力。由于Java中的long类型仍然是无符号的,因此这些方法通过将负值理解为大于MAX_VALUE的值来工作:它模拟了一个无符号的long。parseUnsignedLong说:

unsigned integer将通常与负数相关的值映射为大于MAX_VALUE的正数。

如果你输出的longparseUnsignedLong的结果,并且它是负的,这意味着该值大于语言定义的最大长度值,但是以unsigned long作为参数的方法将正确地解释这些值,就好像它们大于最大值一样。因此,如果您将该数字传递给toUnsignedString,而不是直接打印它,您将得到正确的输出,如另一个答案所示。并非所有这些方法都是Java 8的新方法,例如toHexString也将给定的long解释为16进制的unsigned long,并且打印Long.toHexString(Long.parseUnsignedLong("FBD626CC4961A4FC", 16))将返回正确的十六进制字符串。

parseUnsignedLong将抛出异常,只有当值不能表示为unsigned long类型,即根本不是数字,或者大于264-1(而不是263-1,这是有符号long类型的最大值)。

是的,当您试图打印它时,它会溢出,因为它被转换为Java long类型。为了理解为什么,我们取dec值的log2。

首先,原始值是18146734407382312188。它的log2是~63.9763437545

第二,查看文档:在java中,long类型表示值为-2^63,最大值为2^63-1。

所以,你的值明显大于2^63-1,因此它溢出:

-2^63 + (18146734407382312188 - 2^63 + 1) = -300009666327239428

但是正如@Keiwan出色地提到的,您仍然可以使用Long.toUnsignedString(number);

打印正确的值。

内部无符号数和有符号数以相同的方式表示,即在long的情况下为8字节。区别只是"符号"位的解释方式,即如果你在C/c++程序中做同样的事情,并将你的值存储到uint64_t中,然后将其转换/映射到指定的int64_t中,你应该得到相同的结果。

由于8字节或64位可以容纳的最大值是2^64-1,这是此类数字的硬约束。此外,Java不直接支持无符号数,因此在long中存储unsigned long的唯一方法是允许一个高于有符号Long.MAX_VALUE的值。事实上,Java不知道你正在读取的字符串/十六进制码是表示有符号的还是无符号的,所以由你来提供这种解释,要么转换回字符串,要么使用更大的数据类型,如BigInteger

相关内容

最新更新