2016-07-12 16 views
5

Dato il seguente codice, imballaggio di quattro valori byte in un valore uint.Operazioni matematiche su valori numerici preconfezionati

private static void Pack(byte x, byte y, byte z, byte w) 
{ 
    this.PackedValue = (uint)x | 
         ((uint)y << 8) | 
         ((uint)z << 16) | 
         ((uint)w << 24); 
} 

E 'possibile applicare gli operatori matematici come *, +,/and - sul valore in modo che possa essere scompattato nella corretta byte equivalente?

MODIFICA.

per chiarire, se tento di moltiplicare il valore per un altro imballato

uint result = this.PackedValue * other.PackedValue 

Poi decomprimere utilizzando il seguente ...

public byte[] ToBytes() 
{ 
    return new[] 
    { 
     (byte)(this.PackedValue & 0xFF), 
     (byte)((this.PackedValue >> 8) & 0xFF), 
     (byte)((this.PackedValue >> 16) & 0xFF), 
     (byte)((this.PackedValue >> 24) & 0xFF) 
    }; 
} 

ottengo i risultati errati.

Ecco un esempio di codice completo che mostra il risultato previsto ed effettivo.

void Main() 
{ 
    uint x = PackUint(128, 128, 128, 128); 
    uint y = (uint)(x * 1.5f); 

    byte[] b1 = ToBytes(x); 
    x.Dump(); // 2155905152 
    b1.Dump(); // 128, 255, 128, 255 RIGHT! 
    byte[] b2 = ToBytes(y); 
    b2.Dump(); // 0, 192, 192, 192 WRONG! Should be 192, 192, 192, 192 

} 

// Define other methods and classes here 
private static uint PackUint(byte x, byte y, byte z, byte w) 
{ 
    return ((uint)x) | 
      ((uint)y << 8) | 
      ((uint)z << 16) | 
      ((uint)w << 24); 
} 

public static byte[] ToBytes(uint packed) 
{ 
    return new[] 
    { 
     (byte)(packed & 0xFF), 
     (byte)((packed >> 8) & 0xFF), 
     (byte)((packed >> 16) & 0xFF), 
     (byte)((packed >> 24) & 0xFF) 
    }; 
} 
+0

Perché non ci provi? –

+1

@roryap Ho ma sto riportando i valori sbagliati, qualcosa sta traboccando. –

+2

Ahhh, beh, direi che dovresti includere quel po 'nella tua domanda. –

risposta

5

L'unico motivo per cui non funziona per 1.5f è perché i float non sono abbastanza precisi. Prova 1.5d (per double) e il tuo esempio funzionerà. Tuttavia questo approccio è limitato a casi "carini", cioè a quelli in cui il risultato in ciascun byte è garantito come un numero intero. Un caso speciale è quando si moltiplica per un numero intero, che funzionerà sempre finché non viene superato nessuno dei quattro risultati.

È inoltre possibile eseguire questa operazione per l'addizione e la sottrazione, a condizione che nessuno dei singoli byte trabocchi. Ovviamente ogni overflow rovinerà i byte vicini. Questo è particolarmente problematico se si desidera utilizzare il complemento a 2 per i byte negativi (-128 .. 127) perché aggiungere 3 a -2 è anche un "overflow" e rovinerà il byte successivo.

+0

Grazie per questo, più informativo! –

+1

In realtà ... Vieni a pensarci, l'OP vuole più 4 punti insieme in un solo passaggio senza overflow. Penso che durante gli anni '90 Intel abbia fatto un gran clamore su come ci fosse una nuova funzionalità del processore per questo ... https://blogs.msdn.microsoft.com/dotnet/2014/11/05/using-system-numerics-vector -for-graphics-programming/ – Aron

+0

@Aron Buon punto. internamente in Vector ci sarà il codice che gestisce questo scenario. –

0

Una soluzione molto più interessante al tuo problema è l'utilizzo di Blitting.

void Main() 
{ 
    Byte X = 0x13; 
    Byte Y = 0x6A; 
    Byte Z = 0xA3; 
    Byte W = 0x94; 

    Foo foo = new Foo(X, Y, Z, W); 
    uint i = foo ; 

    Foo bar = (uint)(i * 1.5d); 

    Console.WriteLine(X * 1.5d == bar.X); 
    Console.WriteLine(Y * 1.5d == bar.Y); 
    Console.WriteLine(Z * 1.5d == bar.Z); 
    Console.WriteLine(W * 1.5d == bar.W); 
} 

[StructLayout(LayoutKind.Explicit)] 
public struct Foo 
{ 
    [FieldOffset(0)] 
    public byte X; 

    [FieldOffset(1)] 
    public byte Y; 

    [FieldOffset(2)] 
    public byte Z; 

    [FieldOffset(3)] 
    public byte W; 

    [FieldOffset(0)] 
    public uint Value; 


    public Foo(byte x, byte y, byte z, byte w) : this() 
    { 
     X = x; 
     Y = y; 
     Z = z; 
     W = w; 
    } 

    public static implicit operator Foo(uint value) 
    { 
     return new Foo(){ Value = value }; 
    } 

    public static implicit operator uint(Foo foo) 
    { 
     return foo.Value; 
    } 

} 

Creiamo un nuovo tipo che, invece di non po 'di spostamento, si dà (sicuro tipo) accesso diretto l'indirizzo di memoria che si trova all'interno del uint.

+0

Ma come si moltiplicano X, Y, Z e W di 1,5 in una volta? Penso che fosse la domanda di OP. –

+0

@romkyns 'uint i = 1.5f * foo.Value;' Ma ovviamente vale lo stesso disclaimer sulla precisione e l'overflow. È molto più facile da leggere. – Aron

+0

@Aron Blitting è sicuramente un approccio molto più semplice e pulito alla gestione della struttura nel suo complesso. –

Problemi correlati