题目地址:https://leetcode.com/problems/convert-a-number-to-hexadecimal/
Given an integer, write an algorithm to convert it to hexadecimal. For negative integer, two’s complement method is used.
Note:
- All letters in hexadecimal (a-f) must be in lowercase.
- The hexadecimal string must not contain extra leading 0s. If the number is zero, it is represented by a single zero character ‘0’; otherwise, the first character in the hexadecimal string will not be the zero character.
- The given number is guaranteed to fit within the range of a 32-bit signed integer.
- You must not use any method provided by the library which converts/formats the number to hex directly.
Example 1:
Input:
26
Output:
"1a"
Example 2:
Input:
-1
Output:
"ffffffff"
题目是将十进制转换为十六进制,整数还是比较好处理的,但是这个题目的关键是负数,要理解负数在计算机中是如何表示的:
public class ConvertANumberToHexadecimal {
public static final String[] digit= {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
public String toHex(int num) {
StringBuilder reslut = new StringBuilder("");
if (num > 0) {
while (num != 0) {
reslut.insert(0, digit[num % 16]);
num /= 16;
}
} else if (num == 0){
reslut.insert(0, "0");
} else {
// 以下是截取十六进制每位对应的二进制数值
// 15 to binnary: 0000 0000 0000 0000 0000 0000 0000 1111
reslut.insert(0, digit[num & 15]);
// 240 to binnary: 0000 0000 0000 0000 0000 0000 1111 0000
reslut.insert(0, digit[(num & 240) >> 4]);
// 3840 to binnary: 0000 0000 0000 0000 0000 1111 0000 0000
reslut.insert(0, digit[(num & 3840) >> 8]);
// 61440 to binnary: 0000 0000 0000 0000 1111 0000 0000 0000
reslut.insert(0, digit[(num & 61440) >> 12]);
// 983040 to binnary: 0000 0000 0000 1111 0000 0000 0000 0000
reslut.insert(0, digit[(num & 983040) >> 16]);
// 15728640 to binnary: 0000 0000 1111 0000 0000 0000 0000 0000
reslut.insert(0, digit[(num & 15728640) >> 20]);
// 251658240 to binnary: 0000 1111 0000 0000 0000 0000 0000 0000
reslut.insert(0, digit[(num & 251658240) >> 24]);
// 1879048192 to binnary: 0111 0000 0000 0000 0000 0000 0000 0000
reslut.insert(0, digit[((num & 1879048192) >> 28) + 8]);
}
return reslut.toString();
}
public static void main(String[] args) {
ConvertANumberToHexadecimal hexadecimal = new ConvertANumberToHexadecimal();
System.out.println(hexadecimal.toHex(-13));
}
}
其实在JDK中已经封装好了十进制转十六进制的方法,它是这么写的:
Integer.toHexString(int i)
那么我们看一下JDK是怎么实现的:
/**
* Returns a string representation of the integer argument as an
* unsigned integer in base 16.
*
* <p>The unsigned integer value is the argument plus 2<sup>32</sup>
* if the argument is negative; otherwise, it is equal to the
* argument. This value is converted to a string of ASCII digits
* in hexadecimal (base 16) with no extra leading
* {@code 0}s.
*
* <p>The value of the argument can be recovered from the returned
* string {@code s} by calling {@link
* Integer#parseUnsignedInt(String, int)
* Integer.parseUnsignedInt(s, 16)}.
*
* <p>If the unsigned magnitude is zero, it is represented by a
* single zero character {@code '0'} ({@code '\u005Cu0030'});
* otherwise, the first character of the representation of the
* unsigned magnitude will not be the zero character. The
* following characters are used as hexadecimal digits:
*
* <blockquote>
* {@code 0123456789abcdef}
* </blockquote>
*
* These are the characters {@code '\u005Cu0030'} through
* {@code '\u005Cu0039'} and {@code '\u005Cu0061'} through
* {@code '\u005Cu0066'}. If uppercase letters are
* desired, the {@link java.lang.String#toUpperCase()} method may
* be called on the result:
*
* <blockquote>
* {@code Integer.toHexString(n).toUpperCase()}
* </blockquote>
*
* @param i an integer to be converted to a string.
* @return the string representation of the unsigned integer value
* represented by the argument in hexadecimal (base 16).
* @see #parseUnsignedInt(String, int)
* @see #toUnsignedString(int, int)
* @since JDK1.0.2
*/
public static String toHexString(int i) {
return toUnsignedString0(i, 4);
}
然后再看下toUnsignedString0及其内部所涉及到的方法的实现:
/**
* Convert the integer to an unsigned number.
*/
private static String toUnsignedString0(int val, int shift) {
// assert shift > 0 && shift <=5 : "Illegal shift value";
int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);
int chars = Math.max(((mag + (shift - 1)) / shift), 1);
char[] buf = new char[chars];
formatUnsignedInt(val, shift, buf, 0, chars);
// Use special constructor which takes over "buf".
return new String(buf, true);
}
/**
* Returns the number of zero bits preceding the highest-order
* ("leftmost") one-bit in the two's complement binary representation
* of the specified {@code int} value. Returns 32 if the
* specified value has no one-bits in its two's complement representation,
* in other words if it is equal to zero.
*
* <p>Note that this method is closely related to the logarithm base 2.
* For all positive {@code int} values x:
* <ul>
* <li>floor(log<sub>2</sub>(x)) = {@code 31 - numberOfLeadingZeros(x)}
* <li>ceil(log<sub>2</sub>(x)) = {@code 32 - numberOfLeadingZeros(x - 1)}
* </ul>
*
* @param i the value whose number of leading zeros is to be computed
* @return the number of zero bits preceding the highest-order
* ("leftmost") one-bit in the two's complement binary representation
* of the specified {@code int} value, or 32 if the value
* is equal to zero.
* @since 1.5
*/
public static int numberOfLeadingZeros(int i) {
// HD, Figure 5-6
if (i == 0)
return 32;
int n = 1;
if (i >>> 16 == 0) { n += 16; i <<= 16; }
if (i >>> 24 == 0) { n += 8; i <<= 8; }
if (i >>> 28 == 0) { n += 4; i <<= 4; }
if (i >>> 30 == 0) { n += 2; i <<= 2; }
n -= i >>> 31;
return n;
}
/**
* Format a long (treated as unsigned) into a character buffer.
* @param val the unsigned int to format
* @param shift the log2 of the base to format in (4 for hex, 3 for octal, 1 for binary)
* @param buf the character buffer to write to
* @param offset the offset in the destination buffer to start at
* @param len the number of characters to write
* @return the lowest character location used
*/
static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
int charPos = len;
int radix = 1 << shift;
int mask = radix - 1;
do {
buf[offset + --charPos] = Integer.digits[val & mask];
val >>>= shift;
} while (val != 0 && charPos > 0);
return charPos;
}
/*
* Package private constructor which shares value array for speed.
* this constructor is always expected to be called with share==true.
* a separate constructor is needed because we already have a public
* String(char[]) constructor that makes a copy of the given char[].
*/
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}