2011-08-11 25 views
18

Có cách nào dễ dàng và nhanh chóng để chuyển đổi một Java đã ký dài thành một chuỗi dài chưa ký?Java: đã ký dài để unsigned chuỗi dài

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

@parsifal http://en.wikipedia.org/wiki/ Twos_complement –

Trả lời

26

Đây là một giải pháp sử dụng 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(); 
} 

này hoạt động vì giá trị unsigned của một (đã ký) số trong hai s bổ sung chỉ là 2 (số bit) hơn hơn giá trị đã ký, và long của Java có 64 bit.

Và BigInteger có phương thức đẹp toString() mà chúng tôi có thể sử dụng tại đây.

2

Tôi cũng có phiên bản không phải BigInteger (vì phải liên hệ với số BigInteger đã làm lỗi tôi một lúc); Tôi đã giữ lại chức năng main của tôi để dễ bạn thí nghiệm:

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))); 
     } 
    } 
} 
6

Dựa trên @ giải pháp Paulo Ebermann tôi đã đưa ra một này:

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(); 
} 

Sử dụng new BigInteger(int signum, byte[] bytes); làm BigInteger để đọc byte là số dương (unsigned) và áp dụng signum cho nó.


Dựa trên giải pháp Jester-Young @ Chris Tôi thấy điều này một:

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; 
} 

Tuy nhiên, một cách khác để làm điều đó:

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); 
    } 
} 
+0

Tôi rất muốn xem phiên bản "no-'BigInteger'" có nhanh hơn cả hai phiên bản hay không. :-) (Nếu tôi tìm thấy một số thời gian ngày hôm nay, tôi sẽ làm một số thử nghiệm và đăng kết quả của tôi.) –

+0

@Chris thấy phiên bản không có BigInteger của tôi! :) –

+0

+1 Rất đẹp (tái phân chia-by-10). Đó có lẽ là cách đơn giản nhất để làm điều đó. –

-1

Tôi chỉ có vấn đề này và giải quyết nó sử dụng mã này:

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

Tôi không chắc chắn nếu tôi thiếu cái gì nhưng có vẻ như đơn giản hơn rất nhiều theo cách này.

+0

vì vậy nó đầu ra cho -1l là gì? –

+0

ffffffffffffffff; Tôi chỉ nhận ra rằng các poster ban đầu muốn có một chuỗi thập phân và không phải là một chuỗi thập lục phân - xấu của tôi! –

3

Hai năm sau, nhưng đây là giải pháp rất nhỏ gọn giúp tránh BigInteger và mảng byte.
Về cơ bản nó mô phỏng phân chia không dấu để trích xuất một chữ số, và sau đó nó tải phần còn lại vào hàm thư viện.

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); 
} 

Ngoài ra, nếu bạn muốn tránh chuỗi tạm thời và chức năng thư viện hoàn toàn, sau đó chúng ta có thể tính toán tất cả các chữ số từ nguyên tắc đầu tiên:

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); 
} 

Cả hai triển khai trên là chức năng tương đương , vì vậy bạn có thể chọn cái bạn thích nhất.

4

Nếu bạn không muốn phát minh lại bánh xe và duy trì mã của bạn, ổi có thể là một lựa chọn:

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

Tài liệu tham khảo: UnsignedLong, UnsignedLongs

3

Java 8 bao gồm một số hỗ trợ cho chờ đợi unsigned. Nếu bạn không cần phải padding zero, chỉ cần làm:

Long.toUnsignedString(n); 

Nếu bạn cần không đệm, định dạng không làm việc cho chờ đợi unsigned. Tuy nhiên cách giải quyết điều này một chia unsigned 10 để thả các giá trị unsigned đến một điểm mà nó có thể được biểu diễn mà không có chút dấu hiệu trong dài:

String.format("%019d%d", Long.divideUnsigned(n, 10), Long.remainderUnsigned(n, 10)); 
Các vấn đề liên quan