如何将Java long转换为* unsigned * base-X String(并返回)?

时间:2012-03-28 18:23:52

标签: java biginteger unsigned base36

[编辑]我不接受任何涉及BigInteger或其他类似低效方法的答案。在回答之前,请务必阅读问题!

Java,令人讨厌,不支持无符号数字类型。您可以使用下一个更大的类型将byte,short或int转换为unsigned,例如:

short s = -10;
int unsigned_short = s & 0xFFFF;

但你不能长时间这样做,因为没有更大的类型。

那么,在我的情况下,如何将已签名的长整数转换为“无符号”的基数-X,然后返回? Long类具有这些方法,但将long视为signed,只是因为它们是。

我可能会使用一些操作和BigInteger,但BigInteger 非常慢,并通过临时BigInteger创建创建垃圾。我会做很多转换(我想)。我需要一个与Long.toString(long i,int radix)的默认实现一样高效的算法。

尝试调整Long.toString()的代码,我来:

final int RADIX = 36;
final char[] DIGITS = { '0', ... , 'Z' };
long value = 100;
if (value == 0) {
    return "0";
} else {
    char[] buf = new char[13];
    int charPos = 12;
    long i = value;
    while (i != 0) {
        buf[charPos--] = DIGITS[Math.abs((int) (i % RADIX))];
        i /= RADIX;
    }
    return new String(buf, charPos + 1, (12 - charPos));
}

但是它没有正确处理负值,尽管有Math.abs()。

一旦这个工作,我需要反向转换,但我希望它会更容易。欢迎你把它放在你的答案中。

[编辑]实际上,我只看了Long.parseLong(String s,int radix)的代码,它看起来比em.more 复杂,而不是Long.toString(long i,int radix)。

5 个答案:

答案 0 :(得分:8)

    long l = 0xffffffffffffffffL; // any long, e.g. -1

    // to string
    BigInteger bi = new BigInteger(Long.toString(l & ~(1L << 63)));
    if (l < 0) bi = bi.setBit(64);
    final String b36 = bi.toString(36);
    System.out.println("original long:" + l);
    System.out.println("result 36: " + b36);

    // parse
    final BigInteger parsedBi = new BigInteger(b36, 36);

    l = parsedBi.longValue();
    if (parsedBi.testBit(64)) l = l | (1L << 63);
    System.out.println("parsed long = " + l);

基准测试(一百万次操作):

    // toString
    long l = 0x0ffffffffffffeffL;
    {
        final long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) toStringBi(l);
        System.out.println("BigInteger time = " + 
            (System.currentTimeMillis() - start) + " ms.");
    }
    {
        final long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) Long.toString(l, 36);
        System.out.println("Long.toString time = " + 
           (System.currentTimeMillis() - start) + "ms.");
    }
    // Parsing
    final String b36 = toStringBi(l);
    final String long36 = Long.toString(l, 36);
    {
        final long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            final BigInteger parsedBi = new BigInteger(b36, 36);
            l = parsedBi.longValue();
            if (parsedBi.testBit(64)) l = l | (1L << 63);
        }
        System.out.println("BigInteger.parse time = " 
            + (System.currentTimeMillis() - start) + " ms.");
    }
    {
        final long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) Long.parseLong(long36, 36);
        System.out.println("Long.parseLong time = " 
            + (System.currentTimeMillis() - start) + "ms.");
    }
  • BigInteger时间= 1027毫秒。
  • Long.toString time = 244ms。
  • BigInteger.parse time = 297 ms。
  • Long.parseLong time = 132ms。

答案 1 :(得分:2)

另一种选择是使用UnsignedLongs中的Google guava-libraries(还有很多其他好东西):

String s = UnsignedLongs.toString( -1L, Character.MAX_RADIX );

long l = UnsignedLongs.parseUnsignedLong( "2jsu3j", 36 );

从+ EugeneRetunsky(见下文)添加到基准测试中,这在我的机器上给出了以下时间:

  • BigInteger时间(第1次运行)= 1306 ms。
  • BigInteger时间(第二次运行)= 1075 ms。
  • Long.toString time = 422ms。
  • UnsignedLongs.toString time = 445ms。
  • BigInteger.parse time = 298 ms。
  • Long.parseLong time = 164ms。
  • UnsignedLongs.parseUnsignedLong time = 107ms。

出于好奇,我让第一次测试运行两次以检查是否会改善时间。它始终如一(在我的机器上约为400毫秒),也适用于UnsignedLongs。其他选项似乎不再从热点编译器中获利。

public class UnsignedLongsTest {
private static String toStringBi( long l ) {
    BigInteger bi = new BigInteger(Long.toString(l & ~(1L << 63)));
    if (l < 0) {
        bi = bi.setBit(64);
    }
    final String b36 = bi.toString(36);
    return b36;
}

public static void main( String[] args ) {
    // toString
    long l = 0x0ffffffffffffeffL;
    {
        final long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            toStringBi(l);
        }
        System.out.println("BigInteger time (1st run) = " +
                (System.currentTimeMillis() - start) + " ms.");
    }
    {
        final long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            toStringBi(l);
        }
        System.out.println("BigInteger time (2nd run) = " +
                (System.currentTimeMillis() - start) + " ms.");
    }
    {
        final long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            Long.toString(l, 36);
        }
        System.out.println("Long.toString time = " +
           (System.currentTimeMillis() - start) + "ms.");
    }
    {
        final long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            UnsignedLongs.toString(l, 36);
        }
        System.out.println("UnsignedLongs.toString time = " +
                (System.currentTimeMillis() - start) + "ms.");
    }
    // Parsing
    final String b36 = toStringBi(l);
    final String long36 = Long.toString(l, 36);
    {
        final long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            final BigInteger parsedBi = new BigInteger(b36, 36);
            l = parsedBi.longValue();
            if (parsedBi.testBit(64)) {
                l = l | (1L << 63);
            }
        }
        System.out.println("BigInteger.parse time = "
            + (System.currentTimeMillis() - start) + " ms.");
    }
    {
        final long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            Long.parseLong(long36, 36);
        }
        System.out.println("Long.parseLong time = "
            + (System.currentTimeMillis() - start) + "ms.");
    }
    {
        final long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            UnsignedLongs.parseUnsignedLong( long36, 36 );
        }
        System.out.println("UnsignedLongs.parseUnsignedLong time = "
                + (System.currentTimeMillis() - start) + "ms.");
    }
}

答案 2 :(得分:1)

由于尽管“不接受任何涉及BigInteger的答案”,但您接受了BigInteger解决方案,这是另一种BigInteger解决方案。你可以强迫signum始终为正面,而不是掩盖标志:

long input = 0xffffffffffffffffL; // any long, e.g. -1
byte[] bytes = ByteBuffer.allocate(8).putLong(input).array();

String base36 = new BigInteger(1, bytes).toString(36);

答案 3 :(得分:1)

另外,如果你使用long作为一个字节数组,@ JonnyDee有一个算法(在Python中,但它很简短),用于在任何两个基础之间进行转换,如果你认为字节数组是一个数字基数为256位。转换回字节只是将base-36转换为base-256。

https://stackoverflow.com/a/6158278/43217

他的相应博客文章:

https://jonnydee.wordpress.com/2011/05/01/convert-a-block-of-digits-from-base-x-to-base-y/

答案 4 :(得分:1)

问题是你只需要一个有符号的64位divmod就可以找到一个快速无符号的64位divmod。搜索udivmoddi3应该在C中提供一些实现 - 这些实现通常用于在仅支持硬件中的32位divmod的体系结构上执行64位divmod。

请注意,您只需抓住底部数字 - 一旦完成此操作,商数将为正数,您可以使用Long.toString()。

如果基数是偶数(你指出基数为36),你可以得到最低位数而没有太多麻烦(我的数学可能是错误的):

int bottomDigit = ((value>>>1)%(radix/2))<<1)|((int)value&1);
long rest = (value>>>1)/(radix/2);
if (rest == 0)
{
  return Integer.toString(bottomDigit,radix);
}
return Long.toString(rest,radix) + Integer.toString(bottomDigit,radix);

显而易见的进一步优化是,如果值为正,则直接调用Long.toString()