Domanda Funzione C # "non sicura" - * (float *) (& result) vs. (float) (result)


Qualcuno può spiegare in modo semplice i seguenti codici:

public unsafe static float sample(){    
      int result = 154 + (153 << 8) + (25 << 16) + (64 << 24);

      return *(float*)(&result); //don't know what for... please explain
}

Nota: il codice precedente utilizza pericoloso funzione

Per il codice di cui sopra, sto avendo un momento difficile perché non capisco qual è la differenza tra il suo valore restituito confrontato con il valore restituito di seguito:

return (float)(result);

È necessario utilizzare la funzione non sicura se si ritorna *(float*)(&result)?


44
2017-10-04 08:04


origine


risposte:


Su .NET a float è rappresentato utilizzando un IEEE binario32 numero flottante a precisione singola memorizzato utilizzando 32 bit. Apparentemente il codice costruisce questo numero assemblando i bit in un int e poi lo lancia a float utilizzando unsafe. Il cast è ciò che in termini C ++ è chiamato a reinterpret_cast dove nessuna conversione viene eseguita quando viene eseguito il cast - i bit vengono semplicemente reinterpretati come un nuovo tipo.

IEEE single precision floating number

Il numero assemblato è 4019999A in esadecimale o 01000000 00011001 10011001 10011010 in binario:

  • Il bit del segno è 0 (è un numero positivo).
  • I bit esponenti sono 10000000 (o 128) risultante nell'esponente 128 - 127 = 1 (la frazione viene moltiplicata per 2 ^ 1 = 2).
  • I bit della frazione sono 00110011001100110011010 che, se non altro, hanno quasi un modello riconoscibile di zero e uno.

Il float restituito ha gli stessi bit identici a 2.4 convertiti in virgola mobile e l'intera funzione può essere semplicemente sostituita dal letterale 2.4f.

Lo zero finale che tipo di "spezza il pattern di bit" della frazione è forse in grado di far corrispondere il float a qualcosa che può essere scritto usando un letterale a virgola mobile?


Quindi qual è la differenza tra un cast normale e questo strano "cast non sicuro"?

Assumi il seguente codice:

int result = 0x4019999A // 1075419546
float normalCast = (float) result;
float unsafeCast = *(float*) &result; // Only possible in an unsafe context

Il primo cast prende il numero intero 1075419546 e lo converte nella sua rappresentazione in virgola mobile, ad es. 1075419546f. Ciò comporta il calcolo dei bit di segno, esponente e frazione richiesti per rappresentare il numero intero originale come un numero in virgola mobile. Questo è un calcolo non banale che deve essere fatto.

Il secondo cast è più sinistro (e può essere eseguito solo in un contesto non sicuro). Il &result prende l'indirizzo di result restituire un puntatore alla posizione in cui il numero intero 1075419546 è memorizzato. L'operatore di dereferenziazione del puntatore * può quindi essere utilizzato per recuperare il valore puntato dal puntatore. utilizzando *&result recupererà il numero intero memorizzato nella posizione, tuttavia prima lanciare il puntatore a a float* (un puntatore a a float) un float viene invece recuperato dalla posizione di memoria risultante nel float 2.4f essere assegnato a unsafeCast. Quindi la narrativa di *(float*) &result è dammi un puntatore a result e supponiamo che il puntatore sia puntatore a a float e recuperare il valore indicato dal puntatore.

A differenza del primo cast, il secondo cast non richiede alcun calcolo. Sposta semplicemente il 32 bit memorizzato in result in unsafeCast (che per fortuna è anche 32 bit).

In generale, un cast del genere può fallire in molti modi, ma usando unsafestai dicendo al compilatore che sai cosa stai facendo.


75
2017-10-04 08:09



Se sto interpretando ciò che il metodo sta facendo correttamente, questo è un equivalente sicuro:

public static float sample() {    
   int result = 154 + (153 << 8) + (25 << 16) + (64 << 24);

   byte[] data = BitConverter.GetBytes(result);
   return BitConverter.ToSingle(data, 0);
}

Come è stato già detto, è re-interpretare il int valore come a float.


18
2017-10-04 08:11



Questo sembra un tentativo di ottimizzazione. Invece di eseguire calcoli in virgola mobile si stanno eseguendo calcoli integer sulla rappresentazione Integer di un numero in virgola mobile.

Ricorda, i float sono memorizzati come valori binari proprio come gli Ints.

Dopo aver eseguito il calcolo, si stanno usando i puntatori e il cast per convertire il numero intero nel valore float.

Non è lo stesso che lanciare il valore su un float. Ciò trasformerà il valore int 1 in float 1.0. In questo caso, il valore int viene convertito nel numero in virgola mobile descritto dal valore binario memorizzato nell'int.

È abbastanza difficile da spiegare correttamente. Cercherò un esempio :-)

Modificare: Guarda qui: http://en.wikipedia.org/wiki/Fast_inverse_square_root

Il tuo codice sta praticamente facendo lo stesso come descritto in questo articolo.


3
2017-10-04 08:11



Ri: Che cosa sta facendo?

Sta prendendo il valore dei byte memorizzati int e invece interpreta questi byte come float (senza conversione).

Fortunatamente, floats e int sono lo stesso dimensione dei dati di 4 byte.


2
2017-10-04 08:10



Perché Sarge Borsch ha chiesto, ecco l'equivalente "Unione":

[StructLayout(LayoutKind.Explicit)]
struct ByteFloatUnion {
  [FieldOffset(0)] internal byte byte0;
  [FieldOffset(1)] internal byte byte1;
  [FieldOffset(2)] internal byte byte2;
  [FieldOffset(3)] internal byte byte3;
  [FieldOffset(0)] internal float single;
}

public static float sample() {
   ByteFloatUnion result;
   result.single = 0f;
   result.byte0 = 154;
   result.byte1 = 153;
   result.byte2 = 25;
   result.byte3 = 64;

   return result.single;
}

2
2017-09-24 16:33



Come altri hanno già descritto, tratta i byte di un int come se fossero un float.

Potresti ottenere lo stesso risultato senza utilizzare codice non sicuro come questo:

public static float sample()
{
    int result = 154 + (153 << 8) + (25 << 16) + (64 << 24);
    return BitConverter.ToSingle(BitConverter.GetBytes(result), 0);
}

Ma poi non sarà più veloce e si potrebbe usare anche float / doubles e le funzioni Math.


0
2017-10-04 08:21