使用BigInteger将负数解释为unsigned

时间:2012-06-04 19:16:55

标签: java biginteger unsigned

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

例如,我要将-1解释为FFFFFFFFFFFFFFFF

9 个答案:

答案 0 :(得分:6)

尝试使用构造函数

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

答案 1 :(得分:6)

如果您考虑的是二进制补码,则必须指定工作位长度。 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;
}

答案 2 :(得分:3)

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

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

Java BigInteger

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

  • 一个标志,指示数字是:
    • 已签名
    • 积极
  • 一个整数数组,其中包含按big-endian顺序排列的数字的正值。

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

第二,理解BigInteger为十六进制,并根据用于存储的特定位数显示的特定位数与数字值无关,这一点也很重要,但是所有相关的事情带有数字表示。

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

  • -1
  • 4_294_967_295
  • ffffffff

BigInteger不能说相同:

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

读取/解析String并将解释后的值存储为正BigInteger

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

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

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

number = number.mod(maxValue);

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

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

将[负数] BigInteger转换为正数String

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

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

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

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

  • 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转换为十六进制字符串?”

以下代码会将byte[]转换为十六进制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[]

答案 3 :(得分:1)

你总是可以手动做两个补码。如果数字小于0,则反转所有位并加1。

答案 4 :(得分:1)

One Liner(但不要忘记考虑源的持久性问题,可以使用ByteBuffer.byteOrder处理):

new BigInteger(1, ByteBuffer.allocate(Long.SIZE/Byte.SIZE).putLong(Long.parseLong("-1")).array());

答案 5 :(得分:1)

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

BigInteger unsigned(long value) {
    return BigInteger
        .valueOf(value >>> 1).shiftLeft(1) // the upper 63 bits
        .or(BigInteger.valueOf(value & 1L)); // plus the lowest bit
}

答案 6 :(得分:0)

你的意思是什么?

public static void main(String[] args) {

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

答案 7 :(得分:0)

您可以使用此实用程序转换为无符号整数。由于BigIntegers具有无限大小,因此必须指定大小以确定要在翻译中保留多少符号扩展名:

public static BigInteger toPositive(BigInteger num, int sizeInBytes) {
    return num.andNot(BigInteger.valueOf(-1).shiftLeft(sizeInBytes * 8));
}

答案 8 :(得分:0)

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

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

结果:ffffffffffffffff

或简单地说:

System.out.println(Long.toHexString(-1))