นี่คือฟังก์ชั่นที่ฉันใช้สำหรับการคำนวณนี้:
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 สามารถแทนที่ฟังก์ชันคณิตศาสตร์แบบสแตติกสองสามตัวด้วยการใช้งานทางเลือกโดยยึดตาม CPU ภายใน หนึ่งในฟังก์ชั่นเหล่านั้นคือ Integer.numberOfLeadingZeros () ดังนั้นด้วย VM ที่ใหม่กว่าหรือ 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 เช่นเดียวกับการใช้งานอีกสองรายการที่ฉันโพสต์ด้านบน
นี่คือ runtimes จริงบนพีซีของฉัน (Sandy Bridge i7):
ลูกค้า JDK 1.7 32 บิตบิต VM:
binlog: 11.5s
log2nlz: 16.5s
log2fp: 118.1s
log(x)/log(2): 165.0s
เซิร์ฟเวอร์ JDK 1.7 x64 VM:
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 );