如何将Java long转换为*无符号*base-X字符串(以及返回)?
[编辑]我不接受任何涉及BigInteger或其他类似低效方法的答案。请在回答之前仔细阅读问题 令人烦恼的是,Java不支持无符号数字类型。您可以使用下一个较大的类型将短字节或int字节转换为无符号,例如:如何将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
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.");
}
}