[编辑]我不接受任何涉及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)。
答案 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.");
}
答案 1 :(得分:2)
另一种选择是使用UnsignedLongs中的Google guava-libraries(还有很多其他好东西):
String s = UnsignedLongs.toString( -1L, Character.MAX_RADIX );
和
long l = UnsignedLongs.parseUnsignedLong( "2jsu3j", 36 );
从+ EugeneRetunsky(见下文)添加到基准测试中,这在我的机器上给出了以下时间:
出于好奇,我让第一次测试运行两次以检查是否会改善时间。它始终如一(在我的机器上约为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()
。