如何将Java long转换为*无符号*base-X字符串(以及返回)?

如何将Java long转换为*无符号*base-X字符串(以及返回)?,java,biginteger,unsigned,base36,Java,Biginteger,Unsigned,Base36,[编辑]我不接受任何涉及BigInteger或其他类似低效方法的答案。请在回答之前仔细阅读问题 令人烦恼的是,Java不支持无符号数字类型。您可以使用下一个较大的类型将短字节或int字节转换为无符号,例如: short s = -10; int unsigned_short = s & 0xFFFF; 但是你不能用long做这个,因为没有更大的类型 那么,如何将有符号的long转换为“无符号”的base-X,在我的例子中是base-36,然后再转换回来?Long类具有这些方法,但将Lo

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

令人烦恼的是,Java不支持无符号数字类型。您可以使用下一个较大的类型将短字节或int字节转换为无符号,例如:

short s = -10;
int unsigned_short = s & 0xFFFF;
但是你不能用long做这个,因为没有更大的类型

那么,如何将有符号的long转换为“无符号”的base-X,在我的例子中是base-36,然后再转换回来?Long类具有这些方法,但将Long视为有符号的,因为它们是有符号的

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

正在尝试调整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(字符串s,int基数)的代码,它看起来比Long.toString(Long I,int基数)更复杂。

Long l=0xffffffffffffffffL;//任何长的,例如-1
//串
BigInteger bi=新的BigInteger(Long.toString(l&~(1L),因为尽管“不接受任何涉及BigInteger的答案”,您还是接受了BigInteger解决方案,这里是另一个BigInteger解决方案。您可以强制符号始终为正,而不是屏蔽符号:

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

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

此外,如果你使用的是一个字节数组,@ JonnyDee有一个算法(在Python中,但它很短),如果你认为字节数组是一个带有25-6位数的字节,那么在任何两个碱基之间进行转换。将字节转换为BASE-36转换为BASE-256。

以及他相应的博文:


问题在于,您正在寻找一个只提供有符号64位divmod的快速无符号64位divmod。搜索应该会提供一些C语言的实现-这些通常用于在硬件中仅支持32位divmod的体系结构上执行64位divmod

请注意,您只需要抓取底部的数字-完成此操作后,商将为正,您可以使用Long.toString()

如果基数为偶数(您表示基数为36),则可以不费吹灰之力地获得底部数字(我的数学可能是错误的):

int bottomDigit=((值>>>1)%(基数/2))>1)/(基数/2);
if(rest==0)
{
返回整数.toString(底部数字,基数);
}
返回Long.toString(rest,基数)+Integer.toString(bottomDigit,基数);
一个明显的进一步优化是,如果值为正值,则直接调用
Long.toString()

另一个选项是使用(也有许多其他优点):

添加到+EugeneRetunsky(见下文)的基准测试中,这将在我的机器上提供以下时间:

  • BigInteger时间(第一次运行)=1306毫秒
  • BigInteger时间(第二次运行)=1075毫秒
  • Long.toString时间=422ms
  • UnsignedLongs.toString时间=445ms
  • biginger.parse time=298毫秒
  • Long.parseLong time=164ms
  • UnsignedLongs.parseUnsignedLong时间=107ms
出于好奇,我让第一个测试运行了两次,以检查这是否会缩短时间。它一直都会这样做(在我的机器上为~400ms),对于未签名的情况也是如此。其他选项似乎不再从热点编译器中获益

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.");
    }
}
公共类unsignedtest{
私有静态字符串toStringBi(长l){

BigInteger bi=new BigInteger(Long.toString(l&~)好的。您得到+1是因为我可以使用它测试另一个实现,但我不会接受它,因为我已经明确说过解决方案不应该使用BigInteger。我刚刚做了一个基准测试-它比较慢,但它在大约一秒钟内执行一百万次转换(只比Long.toString慢四倍)。如果你想为微优化消磨时间……嗯,我认为不值得这么做。BigInteger在这种情况下很好。+1我必须说,这些结果令人惊讶。“难以置信的慢”我的评论提到了我以前测试过的BigInteger的一些其他用途。我想在这个问题上悬赏,但如果已经有一个答案,那就不可能了。我只需要自己解决它。:/我不认为StackOverflow是这样工作的。它是一个Q/a服务,不是外包公司。你在这里接受教育,而不是有人工作对你来说(甚至是赏金)。我的经验不同。我有一个关于Scala的问题至少一个月没有回答。然后某个第三方对我的问题提出了赏金(我不知道这是可能的),两天左右就得到了回答。这里的人确实把赏金当作“报酬”对于困难的问题。正如@tc.指出的,我的最终解决方案是首先检查标志,并尽可能长时间使用。这为我的要求提供了足够的速度。仅供参考,当我尝试屏蔽解决方案时,我的往返解码失败。我不记得导致它的值。这看起来不快,但这是一个通用解决方案,这是一个好问题d的东西。我实际上正在考虑转移到Base-40,因为你可以用Base-40编码64位长度中相同数量的字符(12),而不是Base-36。有趣的是,你用哪四个字符来表示36以外的字符?大写还是标点符号?我个人非常喜欢Base-36的大小写不敏感。这在URL中很好。我“不要使用URL”…我打算使用标点符号,因为我想用它来表示用户名。添加下划线(表示空格)、破折号、点和撇号将有助于使登录更具可读性。此外,还包括更广泛的内容
long input = 0xffffffffffffffffL; // any long, e.g. -1
byte[] bytes = ByteBuffer.allocate(8).putLong(input).array();

String base36 = new BigInteger(1, bytes).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);
String s = UnsignedLongs.toString( -1L, Character.MAX_RADIX );
long l = UnsignedLongs.parseUnsignedLong( "2jsu3j", 36 );
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.");
    }
}