2012-12-21 17 views
22

Mi piacerebbe avere il segno di un valore di float come un valore int di -1 o 1.Il modo più veloce per ottenere l'accesso a Java?

condizionali Evitare è sempre una buona idea nel ridurre costo computazionale. Per esempio, in un modo mi viene in mente sarebbe quella di utilizzare un digiuno bit-shift per ottenere il segno:

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. 

O più conciso:

int sign = (~(a >> 31) << 1) - 1; 
  1. Ti sembra un buon approccio?
  2. Funzionerà su tutte le piattaforme, data la preoccupazione dell'endianness (come MSB contiene il segno)?

risposta

56

eventuali motivi per cui non si fanno utilizzare semplicemente:

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

Inoltre la maggior parte delle implementazioni numerici hanno un metodo signum prendere una primitiva di quel tipo e restituisce un int, in modo da poter evitare di casting per una performance migliore .

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

Restituirà +1/0/-1 ed è stato ottimizzato per offrire una buona prestazione.

Per riferimento, è possibile dare un'occhiata a the implementation in openJDK. I bit rilevanti sono:

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

Non sapevo questo. Probabilmente hai ragione anche sulle prestazioni. –

+0

+1 È * stato * ottimizzato e può gestire casi come NaN ecc. – arshajii

+7

Che dire di Integer.signum(), Long.signum(), ecc.? Non è necessario eseguire il cast al tipo di associato, quindi. –

5

Si dovrebbe cercare di utilizzare solo difficile da leggere/capire ottimizzazioni, se è assolutamente necessario.

Il problema con

int sign = Math.signum(a); 

può essere che restituisce 0 se 0.0 == un

Ma si dovrebbe fare affidamento su funzioni di libreria già esistenti per quanto possibile per mantenere il vostro codice di facile lettura/comprensione.

Se si desidera 1 per 0,0 == una cosa su questo:

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

La formula restituisce +1 per "Float.NaN". Potrebbe essere accettabile. – assylias

8

Se si desidera solo l'IEEE 754 bit di segno dal valore float che è possibile utilizzare:

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

Questo è molto veloce e ha il vantaggio di non rami. Penso che sia il più veloce che puoi ottenere sulla JVM.

Ma assicurati che sia quello che vuoi! Soprattutto fai attenzione ai casi speciali, ad es. NaN può tecnicamente avere un bit di segno 0 o 1.

Problemi correlati