Это функция, которую я использую для этого вычисления:
public static int binlog( int bits ) // returns 0 for bits=0
{
int log = 0;
if( ( bits & 0xffff0000 ) != 0 ) { bits >>>= 16; log = 16; }
if( bits >= 256 ) { bits >>>= 8; log += 8; }
if( bits >= 16 ) { bits >>>= 4; log += 4; }
if( bits >= 4 ) { bits >>>= 2; log += 2; }
return log + ( bits >>> 1 );
}
Это немного быстрее, чем Integer.numberOfLeadingZeros () (20-30%), и почти в 10 раз быстрее (jdk 1.6 x64), чемреализация на основе Math.log (), подобная этой:
private static final double log2div = 1.000000000001 / Math.log( 2 );
public static int log2fp0( int bits )
{
if( bits == 0 )
return 0; // or throw exception
return (int) ( Math.log( bits & 0xffffffffL ) * log2div );
}
Обе функции возвращают одинаковые результаты для всех возможных входных значений.
Обновление: Java 1.7Сервер JIT способен заменить несколько статических математических функций альтернативными реализациями, основанными на встроенных процессорах.Одной из таких функций является Integer.numberOfLeadingZeros ().Таким образом, при использовании виртуальной машины 1.7 или новее реализация, подобная рассматриваемой, фактически немного быстрее, чем binlog
выше.К сожалению, клиентская JIT, похоже, не имеет этой оптимизации.
public static int log2nlz( int bits )
{
if( bits == 0 )
return 0; // or throw exception
return 31 - Integer.numberOfLeadingZeros( bits );
}
Эта реализация также возвращает те же результаты для всех 2 ^ 32 возможных входных значений, что и две другие реализации, которые я опубликовал выше.
Вот фактическое время выполнения на моем ПК (Sandy Bridge i7):
JDK 1.7 32-битная клиентская виртуальная машина:
binlog: 11.5s
log2nlz: 16.5s
log2fp: 118.1s
log(x)/log(2): 165.0s
JDK 1.7Виртуальный сервер x64:
binlog: 5.8s
log2nlz: 5.1s
log2fp: 89.5s
log(x)/log(2): 108.1s
Это тестовый код:
int sum = 0, x = 0;
long time = System.nanoTime();
do sum += log2nlz( x ); while( ++x != 0 );
time = System.nanoTime() - time;
System.out.println( "time=" + time / 1000000L / 1000.0 + "s -> " + sum );