I'd like to get the sign of a float
value as an int
value of -1 or 1.
Avoiding conditionals is always a good idea in reducing computational cost. For instance, one way I can think of would be to use a fast bit-shift
to get the sign:
float a = ...;
int sign = a >> 31; //0 for pos, 1 for neg
sign = ~sign; //1 for pos, 0 for neg
sign = sign << 1; //2 for pos, 0 for neg
sign -= 1; //-1 for pos, 1 for neg -- perfect.
Or more concisely:
int sign = (~(a >> 31) << 1) - 1;
- Does this seem like a good approach?
- Will this work for all platforms, given endianness concerns (as MSB holds sign)?
Any reasons why you don't simply use:
int sign = (int) Math.signum(a); //1 cast for floating-points, 2 for Integer types
Additionally most Number implementations have a signum method taking a primitive of that type and returning an int, so you can avoid casting for extra performance.
int sign1 = Integer.signum(12); //no casting
int sign2 = Long.signum(-24l); //no casting
It will return +1 / 0 / -1 and it has been optimized to deliver a good performance.
For reference, you can have a look at the implementation in openJDK. The relevant bits are:
public static float signum(float f) {
return (f == 0.0f || isNaN(f)) ? f : copySign(1.0f, f);
}
public static boolean isNaN(float f) {
return (f != f);
}
public static float copySign(float magnitude, float sign) {
return rawCopySign(magnitude, (isNaN(sign) ? 1.0f : sign));
}
public static float rawCopySign(float magnitude, float sign) {
return Float.intBitsToFloat((Float.floatToRawIntBits(sign)
& (FloatConsts.SIGN_BIT_MASK))
| (Float.floatToRawIntBits(magnitude)
& (FloatConsts.EXP_BIT_MASK
| FloatConsts.SIGNIF_BIT_MASK)));
}
static class FloatConsts {
public static final int SIGN_BIT_MASK = -2147483648;
public static final int EXP_BIT_MASK = 2139095040;
public static final int SIGNIF_BIT_MASK = 8388607;
}
If you just want the IEEE 754 sign bit from the float value you can use:
/**
* Gets the sign bit of a floating point value
*/
public static int signBit(float f) {
return (Float.floatToIntBits(f)>>>31);
}
This is very fast and has the advantage of no branches. I think it is the fastest you can get on the JVM.
But make sure it is what you want! Especially watch out for the special cases, e.g. NaN can technically have either a 0 or 1 sign bit.
You should only try to use hard to read/understand optimizations, if it is absolutly neccessary.
The issue with
int sign = Math.signum(a);
may be that it returns 0 if 0.0==a
But you should rely on existing library functions whenever possible to keep your code easy to read/understand.
If you want 1 for 0.0==a what about this:
int sign = (0>a)?-1:1;