2016-07-12 26 views
5

den folgenden Code Verpackung vier byte Werte in eine uint gegeben.Mathematische Operationen an gepackten Zahlenwerte

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

Ist es möglich, mathematische Operatoren wie *, +,/and - auf den Wert in einer Weise anzuwenden, dass sie in die richtige byte Äquivalent ausgepackt werden kann?

BEARBEITEN.

Um zu klären, ob ich den Wert von einem anderen gepackten Wert

uint result = this.PackedValue * other.PackedValue 

Dann entpacken Sie mit dem folgenden ...

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

ich die falschen Ergebnissen zu vermehren versuchen.

Hier ist ein vollständiges Codebeispiel des erwartete und tatsächliches Ergebnis zeigt.

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

Warum Sie es nicht versuchen? –

+1

@roryap Ich habe aber ich bekomme die falschen Werte zurück, etwas überläuft. –

+2

Ahhh, nun, ich würde sagen, Sie sollten dieses Bit in Ihre Frage aufnehmen. –

Antwort

5

Der einzige Grund, warum es für 1.5f nicht funktioniert, ist, weil Schwimmer nicht präzise genug sind. Versuchen Sie 1.5d (für double) und Ihr Beispiel wird funktionieren. Dieser Ansatz ist jedoch auf "nette" Fälle beschränkt, d. H. Diejenigen, bei denen das Ergebnis in jedem Byte garantiert eine ganze Zahl ist. Ein Sonderfall ist, wenn Sie mit einer Ganzzahl multiplizieren, die immer so lange funktioniert, wie keines der vier Ergebnisse überläuft.

Es ist auch möglich, diese zur Verfügung gestellt für die Addition und Subtraktion zu tun, dass keine der einzelnen Bytes Überlauf. Offensichtlich wird jeder Überlauf in der Nähe von Bytes versauen. Dies ist besonders problematisch, wenn Sie das Zweierkomplement für negative Bytes (-128 .. 127) verwenden möchten, da das Hinzufügen von 3 zu -2 ebenfalls ein "Überlauf" ist und das nächste Byte durcheinander bringt.

+0

Danke dafür, sehr informativ! –

+1

Eigentlich ... Wenn man darüber nachdenkt, will das OP mehrere zusammen in einem Schritt ohne Überlauf. Ich denke, dass Intel in den 90er Jahren viel Aufhebens darüber machte, wie es eine neue Prozessor-Funktion dafür gab ... https://blogs.msdn.microsoft.com/dotnet/2014/11/05/using-system-numerics-vector -für-Grafik-Programmierung/ – Aron

+0

@Aron Guter Punkt. intern in Vector wird Code dieses Szenario behandeln. –

0

eine viel schönere Lösung für Ihr Problem ist Blitten zu verwenden.

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

} 

Wir schaffen eine neue Art, die, statt Bitverschiebung der Fall ist, haben Sie direkten (Typ safe) Zugriff auf die Speicheradresse gibt, die innerhalb des uint ist.

+0

Aber wie multipliziert man X, Y, Z und W auf einmal mit 1,5? Ich denke, das war OPs Frage. –

+0

@romkyns 'uint i = 1.5f * foo.Value;' Aber natürlich gilt Ihr gleicher Disclaimer bezüglich Präzision und Überlauf gleichermaßen. Es ist einfach viel einfacher zu lesen. – Aron

+0

@Aron Blitting ist sicherlich ein viel einfacher und sauberer Ansatz, um die Struktur als Ganzes zu verwalten. –