public static int sgn(long value) {
return ((int)(value>>63)) | ((int)((~value)>>>63))
}
I decided to write a test harness to test and time these
contributions:
Here in the output from eclipse:
standard = done with if
Sun = (int) ((i >> 63) | (-i >>> 63)) Long.signum.
signOf = return ( diff != 0 ) ? ( (int) ( diff >>> 32 ) | 1 ) : 0;
sgn = (int) ( diff >> 63 ) ) | ( (int) ( ( ~diff ) >>> 63 );
sgn failed on 0. All the others passed my conformance tests.
On Eclipse the speed the ordering was
standard best, signOf, sgn, sun
Checking conformance on crucial values...
done
Checking conformance on 10 million random longs...
sgn fails at 0 giving 1
done
testing standard signum...
standard: 55984 0
testing Sun's signum...
Sun: 77578 0
testing signOf...
signof: 57469 0
testing sgn...
sgn: 60156 200000000
on Java.exe the speed the ordering was laso
standard best, signOf, sgn, sun
Checking conformance on crucial values...
sgn fails at 0 giving 1
done
Checking conformance on 10 million random longs...
done
testing standard signum...
standard: 49656 0
testing Sun's signum...
Sun: 70438 0
testing signOf...
signof: 53968 0
testing sgn...
sgn: 63235 200000000
finished
with Jet things change drastically:
Now SignOf in a clear winner, twice as fast the runner up and 4 times
as fast as Sun's.
The order is signof, standard, sgn, Sun.
Checking conformance on crucial values...
sgn fails at 0 giving 1
done
Checking conformance on 10 million random longs...
done
testing standard signum...
standard: 31016 0
testing Sun's signum...
Sun: 74390 0
testing signOf...
signof: 16438 0
testing sgn...
sgn: 67953 200000000
finished
The other interesting fact is that is using Jet with signOf is 4.7
times faster than using Sun's Long.signum with Java.exe.
You might wonder why signof is so much faster.
Hint: always use unsigned shifts when you can. Clever optimisers can
use machine instructions for example that will store the high 32 bits
of a long directly to memory or another register. ~ & and | are very
cheap operations, same as + -. conditional jumps are expensive.
Here is the test harness so you can experiment on your own platform.
package com.mindprod.example;
import java.util.Random;
/**
* Compare four different ways of doing a signum.
*
* @author Roedy Green
*/
public class TestSignum {
/**
* crucial values to test for conformance.
*/
private static long testValues[] = {
Long.MIN_VALUE,
Long.MIN_VALUE + 1,
Long.MIN_VALUE + 2,
Integer.MIN_VALUE - 2,
Integer.MIN_VALUE - 1,
Integer.MIN_VALUE,
Integer.MIN_VALUE + 1,
Integer.MIN_VALUE + 2,
-2,
-1,
0,
1,
2,
Integer.MAX_VALUE - 2,
Integer.MAX_VALUE - 1,
Integer.MAX_VALUE,
Integer.MAX_VALUE + 1,
Integer.MAX_VALUE + 2,
Long.MAX_VALUE - 2,
Long.MAX_VALUE - 1,
Long.MAX_VALUE
};
/**
* test all four signum-like algorithms for conformance. To pass
must return
* a + int for a positive long, a -ve int for a negitive long and
0 for a
* zero long.
*
* @param diff
* number to be collapsed to an int preserving sign and
zeroness.
*/
static void checkConforms ( long diff )
{
int standard = signum( diff );
// Sun's Long.signum is implemented as (int) ((i >> 63) | (-i
int sun = Long.signum( diff );
int signOf = signOf( diff );
int sgn = sgn( diff );
if ( signum( sun ) != standard )
{
System.err.println( "sun fails at " + diff + " giving " +
sun );
}
if ( signum( signOf ) != standard )
{
System.err
.println( "signOf fails at " + diff + " giving " +
signOf );
}
if ( signum( sgn ) != standard )
{
System.err.println( "sgn fails at " + diff + " giving " +
sgn );
}
}
/**
* Test harness to compare the various signum methods.
*
* @param args
* not used
*/
public static void main ( String[] args )
{
System.out.println( "Checking conformance on crucial
values..." );
for ( long diff : testValues )
{
checkConforms( diff );
}
System.out.println( "done" );
System.out
.println( "Checking conformance on 10 million random
longs..." );
Random wheel = new Random();
for ( int i = 0; i < 10000000; i++ )
{
checkConforms( wheel.nextLong() );
}
System.out.println( "done" );
/** timing test */
/** run timing test 200 million times */
timeStandard();
timeSun();
timeSignOf();
timeSgn();
System.out.println( "finished" );
}
/** how many times to run the timing tests */
final static int timingIterations = 200000000;
/**
* Time the standard signum implementation
*/
private static void timeStandard ()
{
System.out.println( "testing standard signum..." );
int sum = 0;
long start = System.currentTimeMillis();
for ( int i = 0; i < timingIterations; i++ )
{
for ( long diff : testValues )
{
sum += signum( diff );
}
}
// print sum so code to compute it won't be optimised away
System.out.println( "standard: "
+ ( System.currentTimeMillis() - start )
+ " "
+ sum );
}
/**
* Time the signOf signum implementation
*/
private static void timeSignOf ()
{
System.out.println( "testing signOf..." );
int sum = 0;
long start = System.currentTimeMillis();
for ( int i = 0; i < timingIterations; i++ )
{
for ( long diff : testValues )
{
sum += signOf( diff );
}
}
// print sum so code to compute it won't be optimised away
System.out.println( "signof: "
+ ( System.currentTimeMillis() - start )
+ " "
+ sum );
}
/**
* Time the signOf signum implementation
*/
private static void timeSgn ()
{
System.out.println( "testing sgn..." );
int sum = 0;
long start = System.currentTimeMillis();
for ( int i = 0; i < timingIterations; i++ )
{
for ( long diff : testValues )
{
sum += sgn( diff );
}
}
// print sum so code to compute it won't be optimised away
System.out.println( "sgn: "
+ ( System.currentTimeMillis() - start )
+ " "
+ sum );
}
/**
* Time the sun's signum implementation
*/
private static void timeSun ()
{
System.out.println( "testing Sun's signum..." );
int sum = 0;
long start = System.currentTimeMillis();
for ( int i = 0; i < timingIterations; i++ )
{
for ( long diff : testValues )
{
sum += Long.signum( diff );
}
}
// print sum so code to compute it won't be optimised away
System.out.println( "Sun: "
+ ( System.currentTimeMillis() - start )
+ " "
+ sum );
}
/**
* Thomas Hawtin's algorithm Alternate to signum for use in
compare. Not a
* true signum, since it returns ints other than +-1. Where there
is any
* possibility of overflow, you should compare two longs with <
rather than
* subtraction.
*
* @param diff
* number to be collapsed to an int preserving sign and
zeroness.
* usually represents the difference of two long.
* @return sign of diff, some -ve int, 0 or some -ve int.
*/
public static int sgn ( long diff )
{
return ( (int) ( diff >> 63 ) ) | ( (int) ( ( ~diff ) >>> 63 )
) ;
}
/**
* alternate to signum for use in compare. Not a true signum,
since it
* returns ints other than +-1. Where there is any possibility of
overflow,
* you should compare two longs with < rather than subtraction.
*
* @param diff
* number to be collapsed to an int preserving sign and
zeroness.
* usually represents the difference of two long.
* @return sign of diff, some -ve int, 0 or some -ve int.
*/
public static final int signOf ( long diff )
{
return ( diff != 0 ) ? ( (int) ( diff >>> 32 ) | 1 ) : 0;
}
/**
* Collapse number down to +1 0 or -1 depending on sign. Typically
used in
* compare routines to collapse a difference of two longs to an
int.
*
* @param diff
* number to be collapsed to an int preserving sign and
zeroness.
* usually represents the difference of two long.
* @return true signum of diff, +1, 0 or -1.
*/
public static final int signum ( long diff )
{
if ( diff > 0 )
{
return 1;
}
if ( diff < 0 )
{
return -1;
}
else
{
return 0;
}
} // end signum
}