【问题标题】:Java: signed long to unsigned long stringJava:有符号长到无符号长字符串
【发布时间】:2011-08-11 18:48:41
【问题描述】:

有没有一种简单快捷的方法可以将 Java 有符号长字符串转换为无符号长字符串?

-1                    ->  "18446744073709551615"
-9223372036854775808  ->  "09223372036854775808"
 9223372036854775807  ->  "09223372036854775807"
 0                    ->  "00000000000000000000"

【问题讨论】:

标签: java


【解决方案1】:

Java 8 包括一些对无符号长整数的支持。如果您不需要零填充,只需执行以下操作:

Long.toUnsignedString(n);

如果您需要零填充,则格式化不适用于无符号长整数。但是,此解决方法将无符号除以 10 以将无符号值降至可以在 long 中没有符号位的情况下表示的点:

String.format("%019d%d", Long.divideUnsigned(n, 10), Long.remainderUnsigned(n, 10));

【讨论】:

    【解决方案2】:

    这是一个使用 BigInteger 的解决方案:

    /** the constant 2^64 */
    private static final BigInteger TWO_64 = BigInteger.ONE.shiftLeft(64);
    
    public String asUnsignedDecimalString(long l) {
       BigInteger b = BigInteger.valueOf(l);
       if(b.signum() < 0) {
          b = b.add(TWO_64);
       }
       return b.toString();
    }
    

    这是可行的,因为二进制补码中(有符号)数字的无符号值仅比有符号值多 2(位数),而 Java 的 long 有 64 位。

    BigInteger 有一个很好的toString() 方法,我们可以在这里使用它。

    【讨论】:

      【解决方案3】:

      晚了两年,但这是一个非常紧凑的解决方案,它避免了 BigInteger 和字节数组。
      基本上它模拟无符号除法来提取一位数字,然后将其余部分卸载到库函数中。

      public static String unsignedToString(long n) {
          long temp = (n >>> 1) / 5;  // Unsigned divide by 10 and floor
          return String.format("%019d", temp) + (n - temp * 10);
      }
      

      或者,如果您想完全避免使用临时字符串和库函数,那么我们可以根据第一原理计算所有数字:

      public static String unsignedToString(long n) {
          char[] buffer = new char[20];
          int i = buffer.length - 1;
      
          // Do first iteration specially
          long temp = (n >>> 1) / 5;  // Unsigned divide by 10
          buffer[i] = (char)(n - temp * 10 + '0');
          n = temp;
      
          // Do rest of iterations the normal way
          for (i--; i >= 0; i--) {
              buffer[i] = (char)(n % 10 + '0');
              n /= 10;
          }
      
          return new String(buffer);
      }
      

      上述两种实现在功能上是等效的,因此您可以选择最喜欢的一种。

      【讨论】:

        【解决方案4】:

        1

        基于@Paŭlo Ebermann 解决方案,我想出了这个:

        public static String convert(long x) {
            return new BigInteger(1, new byte[] { (byte) (x >> 56),
                (byte) (x >> 48), (byte) (x >> 40), (byte) (x >> 32),
                (byte) (x >> 24), (byte) (x >> 16), (byte) (x >> 8),
                (byte) (x >> 0) }).toString();
        }
        

        使用new BigInteger(int signum, byte[] bytes); 使 BigInteger 将字节读取为正数(无符号)并对其应用符号。


        2

        基于@Chris Jester-Young 解决方案,我找到了这个:

        private static DecimalFormat zero = new DecimalFormat("0000000000000000000");
        
        public static String convert(long x) {
            if (x >= 0) // this is positive
                return "0" + zero.format(x);
        
            // unsigned value + Long.MAX_VALUE + 1
            x &= Long.MAX_VALUE;
            long low = x % 10 + Long.MAX_VALUE % 10 + 1;
            long high = x / 10 + Long.MAX_VALUE / 10 + low / 10;
            return zero.format(high) + low % 10;
        }
        

        3

        另一种方法:

        private static DecimalFormat zero19 = new DecimalFormat("0000000000000000000");
        
        public static String convert(long x) {
            if (x >= 0) {
                return "0" + zero19.format(x);
            } else if (x >= -8446744073709551616L) {
                // if:   x + 18446744073709551616 >= 10000000000000000000
                // then: x + 18446744073709551616 = "1" + (x + 8446744073709551616)
                return "1" + zero19.format(x + 8446744073709551616L);
            } else {
                // if:   x + 18446744073709551616 < 10000000000000000000
                // then: x + 18446744073709551616 = "09" + (x + 9446744073709551616)
                // so:   9446744073709551616 == -9000000000000000000L
                return "09" + (x - 9000000000000000000L);
            }
        }
        

        【讨论】:

        • 我很想看看“no-BigInteger”版本是否也比两个版本都快。 :-)(如果我今天有时间,我会做一些测试并发布我的结果。)
        • @Chris 看看我的非 BigInteger 版本! :)
        • +1 非常好(重新除以 10)。这可能是最直接的方法。
        • 我想过写一些像你的第一个版本的东西,但是对于我的懒惰来说它太复杂了:-)
        【解决方案5】:

        如果您不想重新发明轮子并维护您的代码,Guava 可能是一个选择:

        formatted = UnsignedLong.fromLongBits(myLongValue).toString();
        formatted = UnsignedLongs.toString(myLongValue);
        

        参考:UnsignedLongUnsignedLongs

        【讨论】:

          【解决方案6】:

          我还有一个不基于BigInteger 的版本(因为不得不联系BigInteger 确实困扰了我一段时间);为了便于测试,我保留了我的 main 函数:

          public class UlongToString {
              private static final String MIN_VALUE = "" + Long.MIN_VALUE;
          
              public static String ulongToString(long value) {
                  long pos = value & Long.MAX_VALUE;
                  if (value == pos)
                      return String.valueOf(pos);
          
                  char[] chars = MIN_VALUE.toCharArray();
                  chars[0] = '0';
                  for (int i = chars.length - 1; i != 0 && pos != 0; --i) {
                      if ((chars[i] += pos % 10) > '9') {
                          chars[i] -= 10;
                          ++chars[i - 1];
                      }
                      pos /= 10;
                  }
                  int strip = '1' - chars[0];
                  return new String(chars, strip, chars.length - strip);
              }
          
              public static void main(String... args) {
                  for (String arg : args) {
                      System.out.println(ulongToString(Long.parseLong(arg)));
                  }
              }
          }
          

          【讨论】:

            【解决方案7】:

            我刚遇到这个问题并使用以下代码解决了它:

            String.format("%016x", x);
            

            我不确定我是否遗漏了什么,但这种方式似乎更简单。

            【讨论】:

            • 那么 -1l 输出什么?
            • ffffffffffffffff ;我刚刚意识到原始海报想要一个十进制字符串而不是十六进制字符串 - 我的错!
            猜你喜欢
            • 2011-08-09
            • 1970-01-01
            • 2017-11-08
            • 2017-02-24
            • 1970-01-01
            • 2016-05-22
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多