简体   繁体   中英

Fastest way to get sign in Java?

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;
  1. Does this seem like a good approach?
  2. 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, eg 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 absolutely 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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM