2010-12-17 17 views
8

Me gustaría manipular la representación bit a bit de los números de coma flotante en C#. BinaryWriter y BinaryReader hacerlo de esta manera:Cómo obtener los bits de un "doble" como "largo"

public virtual unsafe void Write(double value) 
{ 
    ulong num = *((ulong*) &value); 
    ... 
} 
public virtual unsafe double ReadDouble() 
{ 
    ... 
    ulong num3 = ...; 
    return *((double*) &num3); 
} 

¿Hay una manera de hacer esto sin código no seguro, y sin la sobrecarga de utilizar realmente BinaryWriter y BinaryReader?

+0

Si le preocupa la velocidad, usar un enfoque de unión es casi tres veces más rápido que llamar a las API 'BitConverter' en mis pruebas rápidas. –

Respuesta

3

¿Estás tratando de evitar el código no seguro por completo, o hacer ¿Quiere una alternativa a esos métodos específicos en BinaryReader y BinaryWriter?

Usted podría utilizar BitConverter.DoubleToInt64Bits y BitConverter.Int64BitsToDouble, que están diseñados para hacer exactamente lo que necesita, aunque creo que utilizan la misma conversión insegura detrás de las escenas como los métodos BinaryReader/BinaryWriter.

+0

¡Buena respuesta! Me pregunto cómo me perdí esa cuando busqué BitConverter. Evito el código inseguro donde sea posible para poder volver a usar el código en, por ejemplo, Silverlight, pero me gusta usar el código verificable más rápido posible. – Qwertie

6

Puede utilizar byte[] BitConverter.GetBytes(double) y long BitConverter.ToInt64(byte[],int) (que pasa a 0 como el start-index), pero internamente IIRC éstos utilizar código no seguro, además de tener la sobrecarga de una matriz. Escoger su veneno ...

+0

Lo suficientemente bueno para mí, supongo. Veo que BitConverter también tiene un byte [] -> largo convertidor que es probablemente más rápido que combinar los bytes manualmente. – Qwertie

+0

Creo que puede hacer esto sin ninguna llamada API o código inseguro. –

7

Otra forma es utilizar una estructura a medida con diseño explícito que define tanto un long y una double en el desplazamiento 0. Esto es equivalente a un union en C.

Algo como esto:

using System.Runtime.InteropServices; 

[StructLayout(LayoutKind.Explicit)] 
struct DoubleLongUnion 
{ 
    [FieldOffset(0)] 
    public long Long; 
    [FieldOffset(0)] 
    public double Double; 
} 

A continuación, utilice la siguiente:

var union = new DoubleLongUnion(); 
union.Double = 1.234d; 
var longBytes = union.Long; 

esto evitará cualquier código no seguro y debe realizar muy rápido también a medida que realiza la conversión en la pila.

No he probado/compilado esta pero creo que debería funcionar :)

EDITAR

simplemente he intentado esto y funciona. El valor de longBytes anterior es 4608236261112822104.

Algunos otros valores:

0d    -> 0L 
double.NaN  -> -2251799813685248L 
double.MinValue -> -4503599627370497L 
double.MaxValue -> 9218868437227405311L 

He aquí un método que hace lo que quiere:

public static long DoubleToLong(double d) 
{ 
    return new DoubleLongUnion { Double = d }.Long; 
} 
+0

En muchos sentidos, los sindicatos son una forma de código inseguro, pero esa es una buena solución :) –

+0

No creo que haya nada particularmente inseguro aquí. Al menos no de acuerdo con el significado no verificable de 'inseguro' en C#. Creo que inseguro es un nombre inapropiado en .NET realmente. –

+0

@Drew - IIRC al menos un marco (podría ser CF, SL o MF) se niega a tocar uniones debido a su naturaleza. –

Cuestiones relacionadas