4

是否可以使用 Java 将负数解析为无符号值BigInteger

因此,例如,我将解释-1FFFFFFFFFFFFFFFF.

4

9 回答 9

11

尝试使用构造函数

public BigInteger(int signum, byte[] magnitude)

第一个参数应设置为 1 以指定您要创建一个正数。字节数组是您在 BIG ENDIAN ORDER 中解析的数字。如果您将第一个参数设置为 1,它应该被解释为一个无符号数。唯一的技巧是将您的数字放入一个字节数组中,但这应该不会太难。

编辑:看起来你必须在这里做一些手动位算术。如果我正确理解您的问题,您需要将 String 解释为 Long,然后将其解释为 unsigned 并将其存储在 BigInteger 类中。我会这样做。

public BigInteger getValue(String numberString)
{
   Long longValue = Long.valueOf(numberString);
   byte [] numberAsArray = new byte[8];  
   for(int i = 0; i < 8; i++)
   {  
      numberAsArray[7 - i] = (byte)((longValue >>> (i * 8)) & 0xFF);
   }
   return new BigInteger(1, numberAsArray);
}  
于 2012-06-04T19:23:56.747 回答
7

如果您正在考虑二进制补码,则必须指定工作位长度。Java long 有 64 位,但 BigInteger 是无界的。

你可以这样做:

// Two's complement reference: 2^n . 
// In this case, 2^64 (so as to emulate a unsigned long)
private static final BigInteger TWO_COMPL_REF = BigInteger.ONE.shiftLeft(64);

public static BigInteger parseBigIntegerPositive(String num) {
    BigInteger b = new BigInteger(num);
    if (b.compareTo(BigInteger.ZERO) < 0)
        b = b.add(TWO_COMPL_REF);
    return b;
}

public static void main(String[] args) {
    System.out.println(parseBigIntegerPositive("-1").toString(16));
}

但这隐含地意味着您正在使用 0 - 2^64-1 范围内的 BigIntegers。

或者,更一般地说:

public static BigInteger parseBigIntegerPositive(String num,int bitlen) {
    BigInteger b = new BigInteger(num);
    if (b.compareTo(BigInteger.ZERO) < 0)
        b = b.add(BigInteger.ONE.shiftLeft(bitlen));
    return b;
}

为了使它更加万无一失,您可以添加一些检查,例如

public static BigInteger parseBigIntegerPositive(String num, int bitlen) {
    if (bitlen < 1)
        throw new RuntimeException("Bad bit length:" + bitlen);
    BigInteger bref = BigInteger.ONE.shiftLeft(bitlen);
    BigInteger b = new BigInteger(num);
    if (b.compareTo(BigInteger.ZERO) < 0)
        b = b.add(bref);
    if (b.compareTo(bref) >= 0 || b.compareTo(BigInteger.ZERO) < 0 )
        throw new RuntimeException("Out of range: " + num);
    return b;
}
于 2012-06-04T20:20:59.503 回答
3

该问题有两个答案,具体取决于 OP 是否希望答案与以下内容有关:

  1. 读取/解析 aString并将解释的值存储为正数BigInteger
  2. 将负数作为正十六进制数输出BigInteger到 a 。String

爪哇BigInteger

首先,重要的是要了解BigInteger实际上将数字存储为两个单独的值:

  • 指示数字为的标志:
    • 积极的
  • 一个整数数组,其中包含按大端顺序排列的数字的正值。

这意味着-1存储为{signed, 1}

其次,同样重要的是要了解,BigInteger根据用于存储的特定位数,将 a 显示为具有特定位数的十六进制与数字值无关,而与数字表示有关。

也就是说,对于二进制补码 32 位值(即int),以下表示都表示内存中存储的相同值:

  • -1
  • 4_294_967_295
  • 噗噗噗

虽然不能这样说BigInteger

  • -1(存储为{negative, 1}
  • 4_294_967_295(存储为{positive, 4_294_967_295}
  • ffffffff(存储为{positive, 4_294_967_295}

读取/解析 aString并将解释值存储为正数BigInteger

要读取String可能包含负数的 a 并将其BigInteger作为具有特定位数的正数存储在 a 中,请使用以下代码:

BigInteger number = new BigInteger("-1");
int bits = 64;

BigInteger maxValue = BigInteger.valueOf(number).shiftLeft(bits);

number = number.mod(maxValue);

例如,使用 16 位的值 -1BigInteger将存储为:

  • {正,65535}(十六进制的ffff)

将 [负]BigInteger转换为正十六进制String

要将 aBigInteger作为调整为特定位数的十六进制数输出,请使用以下代码:

BigInteger number = new BigInteger("-1");
int bits = 64;

BigInteger maxValue = BigInteger.valueOf(number).shiftLeft(bits);
number = number.mod(maxValue);

String outString String.format("%0" + (bits + 3)/4 + "X", number);

表现

如果您担心上面的代码使用除法来获得结果,那么下面的代码将对可以在范围内表示的数字执行相同的操作(即可以使用指定的位数存储):

BigInteger number = new BigInteger("-1");
int bits = 64;

if (number.signum() < 0) {
    number = BigInteger.valueOf(1).shiftLeft(bits).add(number);
}

String outStr = String.format("%0" + (bits + 3)/4 + "X", number);

请注意,上面显示的代码不处理超出预期位范围的数字。

例如 1_000_000 和 -1_000_000 作为 16 位数字将被转换为字符串:

  • F4240
  • -E4240

预期值应该在哪里(取模版本实际返回):

  • 4240
  • BDC0

完整的解决方案

以下代码将上述所有内容组合成一种方法:

public String AsAFixedBitsNumber(BigInteger number, int bits) {
    BigInteger maxValue = BigInteger.valueOf(1).shiftLeft(bits);

    if (maxValue.negate().compareTo(number) >= 0 || maxValue.compareTo(number) <= 0) {
        number = number.mod(maxValue);
    } else if (number.signum() < 0) {
        number = maxValue.add(number);
    }

    return String.format("%0" + (bits + 3)/4 + "X", number);
}

缓冲到 [ BigInteger] 十六进制表示

作为相关问题的额外奖励答案:“如何转换byte[]为十六进制字符串 [使用BigInteger]?”

以下代码将 a 转换byte[]为 hex String

byte[] buffer = new byte[]{1, 2, 3};
String hex = String.format("%0" + buffer.length*2 + "X", new BigInteger(1, buffer));

请注意,将 signum 参数设置为 1 非常重要,这样才能BigInteger理解数组中的值将被解析为“无符号”。

另请注意,此方法会创建两个临时缓冲区来创建字符串Character[],如果您正在处理非常大的数组,最好将数组直接解析为 a 。

于 2020-05-22T15:41:15.463 回答
1

您始终可以手动进行补码。如果数字小于 0,则将所有位取反并加一。

于 2012-06-04T19:25:55.263 回答
1

一个 Liner(但是不要忘记考虑源的 endiness 问题,可以使用 ByteBuffer.byteOrder 处理):

new BigInteger(1, ByteBuffer.allocate(Long.SIZE/Byte.SIZE).putLong(Long.parseLong("-1")).array());
于 2012-06-04T19:42:23.523 回答
1

要击败new BigInteger(long)' 将参数解释为以二进制补码编码的方式,您可以首先以避免使用long' 符号位的方式转换数字的高 63 位,然后“添加”最后一位:

BigInteger unsigned(long value) {
    return BigInteger
        .valueOf(value >>> 1).shiftLeft(1) // the upper 63 bits
        .or(BigInteger.valueOf(value & 1L)); // plus the lowest bit
}
于 2019-09-17T23:54:51.880 回答
0

是你什么吗?

public static void main(String[] args) {

    BigInteger bg =  BigInteger.valueOf(-1);        
    System.out.println(Integer.toHexString(bg.intValue()));
}
于 2012-06-04T19:29:39.113 回答
0

您可以使用此实用程序转换为无符号整数。由于 BigInteger 的大小不受限制,因此必须指定一个大小来确定要在翻译中保留多少符号扩展:

public static BigInteger toPositive(BigInteger num, int sizeInBytes) {
    return num.andNot(BigInteger.valueOf(-1).shiftLeft(sizeInBytes * 8));
}
于 2014-03-06T21:24:27.747 回答
0

简单的解决方案来自@Ahmet Karakaya,但应该针对更大的数字进行修复:

BigInteger bg =  BigInteger.valueOf(-1);
System.out.println(Long.toHexString(bg.intValue()));

结果:ffffffffffffffff

或者简单地说:

System.out.println(Long.toHexString(-1))
于 2017-09-28T19:53:11.337 回答