Подтвердить что ты не робот

Самый быстрый способ получить знак в Java?

Я хотел бы получить знак значения float как значение int -1 или 1.

Избегание условностей всегда является хорошей идеей в сокращении вычислительных затрат. Например, одним из способов, я могу думать, было бы использовать быстрый bit-shift для получения знака:

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.

Или более кратко:

int sign = (~(a >> 31) << 1) - 1;
  • Это похоже на хороший подход?
  • Будет ли это работать для всех платформ, учитывая проблемы, связанные с контентом (как MSB имеет знак)?
4b9b3361

Ответ 1

Любые причины, по которым вы не просто используете:

int sign = (int) Math.signum(a); //1 cast for floating-points, 2 for Integer types

Кроме того, большинство реализаций числа имеют метод signum, принимающий примитив этого типа и возвращающий int, поэтому вы можете избежать кастинга для дополнительной производительности.

int sign1 = Integer.signum(12); //no casting
int sign2 = Long.signum(-24l); //no casting

Он вернет +1/0/-1, и он был оптимизирован для обеспечения хорошей производительности.

Для справки вы можете взглянуть на реализацию в openJDK. Соответствующие биты:

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;
}

Ответ 2

Если вы просто хотите, чтобы бит знака IEEE 754 из значения float вы могли использовать:

/**
 * Gets the sign bit of a floating point value
 */
public static int signBit(float f) {
    return (Float.floatToIntBits(f)>>>31);
}

Это очень быстро и имеет преимущество без ветвей. Я думаю, что это самый быстрый способ получить JVM.

Но убедитесь, что это то, что вы хотите! Особенно следите за особыми случаями, например. NaN может технически иметь либо знаковый бит 0, либо 1 знак.

Ответ 3

Вы должны только пытаться использовать трудные для чтения/понимания оптимизации, если это абсолютно необходимо.

Проблема с

int sign = Math.signum(a);

может быть, что он возвращает 0, если 0.0 == a

Но вы всегда должны полагаться на существующие библиотечные функции, чтобы ваш код легко читал/понимал.

Если вы хотите 1 для 0.0 ==, то что об этом:

int sign = (0>a)?-1:1;