I want to convert from char representing a hexadecimal value (in upper or lower case) to byte, like
'0'->0, '1' -> 1, 'A' -> 10, 'a' -> 10, 'f' -> 15 etc...
I will be calling this method extremely often, so performance is important. Is there a faster way than to use a pre-initialized HashMap<Character,Byte>
to get the value from?
Answer
It seems like it's a tossup between using a switch-case and Jon Skeet's direct computing solution - the switch-case solution seems to edge out ever so slightly, though. Greg's array method wins out. Here are the performance results (in ms) for 200,000,000 runs of the various methods:
Character.getNumericValue:
8360
Character.digit:
8453
HashMap<Character,Byte>:
15109
Greg's Array Method:
6656
JonSkeet's Direct Method:
7344
Switch:
7281
Thanks guys!
Benchmark method code
Here ya go, JonSkeet, you old competitor. ;-)
public class ScratchPad {
private static final int NUMBER_OF_RUNS = 200000000;
static byte res;
static HashMap<Character, Byte> map = new HashMap<Character, Byte>() {{
put( Character.valueOf( '0' ), Byte.valueOf( (byte )0 ));
put( Character.valueOf( '1' ), Byte.valueOf( (byte )1 ));
put( Character.valueOf( '2' ), Byte.valueOf( (byte )2 ));
put( Character.valueOf( '3' ), Byte.valueOf( (byte )3 ));
put( Character.valueOf( '4' ), Byte.valueOf( (byte )4 ));
put( Character.valueOf( '5' ), Byte.valueOf( (byte )5 ));
put( Character.valueOf( '6' ), Byte.valueOf( (byte )6 ));
put( Character.valueOf( '7' ), Byte.valueOf( (byte )7 ));
put( Character.valueOf( '8' ), Byte.valueOf( (byte )8 ));
put( Character.valueOf( '9' ), Byte.valueOf( (byte )9 ));
put( Character.valueOf( 'a' ), Byte.valueOf( (byte )10 ));
put( Character.valueOf( 'b' ), Byte.valueOf( (byte )11 ));
put( Character.valueOf( 'c' ), Byte.valueOf( (byte )12 ));
put( Character.valueOf( 'd' ), Byte.valueOf( (byte )13 ));
put( Character.valueOf( 'e' ), Byte.valueOf( (byte )14 ));
put( Character.valueOf( 'f' ), Byte.valueOf( (byte )15 ));
put( Character.valueOf( 'A' ), Byte.valueOf( (byte )10 ));
put( Character.valueOf( 'B' ), Byte.valueOf( (byte )11 ));
put( Character.valueOf( 'C' ), Byte.valueOf( (byte )12 ));
put( Character.valueOf( 'D' ), Byte.valueOf( (byte )13 ));
put( Character.valueOf( 'E' ), Byte.valueOf( (byte )14 ));
put( Character.valueOf( 'F' ), Byte.valueOf( (byte )15 ));
}};
static int[] charValues = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,10, 11, 12, 13,14,15};
static char[] cs = new char[]{'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','A','B','C','D','E','F'};
public static void main(String args[]) throws Exception {
long time = System.currentTimeMillis();
for( int i = 0; i < NUMBER_OF_RUNS; i++ ) {
res = getNumericValue( i );
}
System.out.println( "Character.getNumericValue:" );
System.out.println( System.currentTimeMillis()-time );
time = System.currentTimeMillis();
for( int i = 0; i < NUMBER_OF_RUNS; i++ ) {
res = getDigit( i );
}
System.out.println( "Character.digit:" );
System.out.println( System.currentTimeMillis()-time );
time = System.currentTimeMillis();
for( int i = 0; i < NUMBER_OF_RUNS; i++ ) {
try {
res = getValueFromArray( i );
} catch (IllegalArgumentException e) {
}
}
System.out.println( "Array:" );
System.out.println( System.currentTimeMillis()-time );
time = System.currentTimeMillis();
for( int i = 0; i < NUMBER_OF_RUNS; i++ ) {
res = getValueFromHashMap( i );
}
System.out.println( "HashMap<Character,Byte>:" );
System.out.println( System.currentTimeMillis()-time );
time = System.currentTimeMillis();
for( int i = 0; i < NUMBER_OF_RUNS; i++ ) {
char c = cs[i%cs.length];
res = getValueFromComputeMethod( c );
}
System.out.println( "JonSkeet's Direct Method:" );
System.out.println( System.currentTimeMillis()-time );
time = System.currentTimeMillis();
for( int i = 0; i < NUMBER_OF_RUNS; i++ ) {
res = getValueFromSwitch( i );
}
System.out.println( "Switch:" );
System.out.println( System.currentTimeMillis()-time );
}
private static byte getValueFromSwitch( int i ) {
byte res;
char ch = cs[i%cs.length];
switch( ch ) {
case '0':
res = 0;
break;
case '1':
res = 1;
break;
case '2':
res = 2;
break;
case '3':
res = 3;
break;
case '4':
res = 4;
break;
case '5':
res = 5;
break;
case '6':
res = 6;
break;
case '7':
res = 7;
break;
case '8':
res = 8;
break;
case '9':
res = 9;
break;
case 'a':
case 'A':
res = 10;
break;
case 'b':
case 'B':
res = 11;
break;
case 'c':
case 'C':
res = 12;
break;
case 'd':
case 'D':
res = 13;
break;
case 'e':
case 'E':
res = 14;
break;
case 'f':
case 'F':
res = 15;
break;
default:
throw new RuntimeException("unknown hex character: " + ch );
}
return res;
}
private static byte getValueFromComputeMethod( char c ) {
byte result = 0;
if (c >= '0' && c <= '9')
{
result = (byte)(c - '0');
}
if (c >= 'a' && c <= 'f')
{
result = (byte)(c - 'a' + 10);
}
if (c >= 'A' && c <= 'F')
{
result = (byte)(c - 'A' + 10);
}
return result;
}
private static byte getValueFromHashMap( int i ) {
return map.get( Character.valueOf( cs[i%cs.length] ) ).byteValue();
}
private static byte getValueFromArray( int i ) {
char c = cs[i%cs.length];
if (c < '0' || c > 'f') {
throw new IllegalArgumentException();
}
byte result = (byte)charValues[c-'0'];
if (res < 0) {
throw new IllegalArgumentException();
}
return result;
}
private static byte getDigit( int i ) {
return (byte)Character.digit( cs[i%cs.length], 16 );
}
private static byte getNumericValue( int i ) {
return (byte)Character.getNumericValue( cs[i%cs.length] );
}
}
The conversion of hexadecimal to decimal is done by using the base number 16. The hexadecimal digit is expanded to multiply each digit with the power of 16. The power starts at 0 from the right moving forward towards the right with the increase in power. For the conversion to complete, the multiplied numbers are added.
To convert a hexadecimal string to a numberUse the ToInt32(String, Int32) method to convert the number expressed in base-16 to an integer. The first argument of the ToInt32(String, Int32) method is the string to convert. The second argument describes what base the number is expressed in; hexadecimal is base 16.
An integer can be converted to a hexadecimal by using the string. ToString() extension method.
In Java programs, hexadecimal numbers are written by placing 0x before numbers.
I don't recall seeing this method before, but Mikko Rantanen pointed this equation out in a comment on the question, Code golf - hex to (raw) binary conversion
(char | 32) % 39 - 9
I don't know what it would benchmark as (perhaps someone can add it to the benchmark above and run it, but I'm guessing the % kills the performance) - but it's a neat, simple one-liner for single character hexadecimal to decimal conversion. Handles 0-9, A-F, a-f.
Dude, I'm microcontroller programmer and in this tiny world, speed really matters. The fastest way to convert an 'ASCII' digit to byte (from 'A' to 0x0A) is using this small piece of code
c|=0x20;
return c<='9'? c+0xD0 : c+0xA9;
The magic of this command is simple, if you look the ascii table you'll find numbers starting with 0x3_, and letters at columns 0x4_ and 0x6_ respectively. 1) if you "or" the incoming char (variable "c") with 0x20, you'll be never changing numbers... but for letters you'll be converting uppercase to lowercase. Since we are looking for only A..F(a..f) values... I don't care others. 2) now the magic. To avoid subtractions, I use additive operators. the 0xD0 is the same of -0x30, and 0xA9 is the same of -'a'+10;
the branch instruction generated by the comparison is pretty simple and didn't take the overhead of table lookups and neither "mod" or other operators!
Enjoy...
According to my timings the following outperforms Jon Skeet's already pretty efficient method.
The idea is to take advantage of the test on range 'A'..'F' to also discard one of the ranges '0'..'9' or 'a'..'f'.
public static int hex2Dig2(char c) {
if (c < 'A') {
if (c >= '0' && c <= '9')
return c - '0';
} else if (c > 'F') {
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
} else {
return c - 'A' + 10;
}
return -1; // or throw exception
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With