为什么我不应该从 Long.parseUnsignedLong 得到一个值

Why am I getting a value back from Long.parseUnsignedLong when I shouldn't be

当我做

Long.parseUnsignedLong("FBD626CC4961A4FC", 16)

我回来了-300009666327239428

这似乎是错误的,因为根据这个答案 unsigned long 的含义是范围总是正数。

为了从这个十六进制值中得到正确的数字,我做了

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

当我打印值时,它会打印出正确的值。但如果我这样做 value.longValue()

我还是一样-300009666327239428这是不是数字太大溢出了?

Java 8 确实(有点)支持 unsigned longs,但是,您不能直接打印它们。这样做会给您看到的结果。

如果你有一个 unsigned long

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

你可以用函数得到正确的字符串表示

String numberToPrint = Long.toUnsignedString(number);

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

18146734407382312188

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

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

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

其次,查看documentation:在java中long类型表示的值为-2^63,最大值为2^63-1。

因此,您的值显然大于 2^63-1,因此溢出:

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

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

打印正确的值

内部无符号数和有符号数以相同的方式表示,即在 long 的情况下为 8 个字节。区别仅在于 "sign" 位的解释方式,即如果您在 C/C++ 程序中执行相同操作并将您的值存储到 uint64_t 中,然后将 cast/map 存储到赋值 int64_t 你应该得到相同的结果。

由于 8 字节或 64 位可以容纳的最大值是 2^64-1,因此这是此类数字的硬性约束。此外 Java 不直接支持无符号数,因此在 long 中存储无符号长整型的唯一方法是允许一个高于有符号 Long.MAX_VALUE 的值。事实上,Java 不知道您正在阅读的 string/hexcode 是表示有符号长整数还是无符号长整数,因此您可以通过转换回字符串或使用更大的数据类型,例如 BigInteger.

十六进制数"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* 方法的目的,以提供处理无符号长整数的能力(如 compareUnsigned or divideUnsigned). Since the type long in Java is still unsigned, those methods work by understanding negative values as values greater than MAX_VALUE: it simulates an unsigned long. parseUnsignedLong 说:

An unsigned integer maps the values usually associated with negative numbers to positive numbers larger than MAX_VALUE.

如果您打印 longparseUnsignedLong 的结果,并且它是负数,这意味着该值大于语言定义的最大长值,但是采用无符号长整型作为参数的方法将正确解释这些值,就好像它们大于最大值一样。因此,不是直接打印它,如果您将该数字传递给 toUnsignedString, you'll get the right output, . Not all of these methods are new to Java 8, for example toHexString 也将给定的 long 解释为基数为 16 的无符号长整型,并且打印 Long.toHexString(Long.parseUnsignedLong("FBD626CC4961A4FC", 16)) 将返回您的权利十六进制字符串。

parseUnsignedLong 仅当该值不能表示为无符号长整数时才会抛出异常,即根本不是数字,或者大于 264-1 (而不是 263-1,它是有符号长整数的最大值)。