Reputation: 10645
Given the following code packing four byte
values into a uint
.
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);
}
Is it possible to apply mathematical operators like *, +, / and -
on the value in a manner that it can be unpacked into the correct byte
equivalent?
EDIT.
To clarify, if I attempt to multiply the value by another packed value
uint result = this.PackedValue * other.PackedValue
Then unpack using the following...
public byte[] ToBytes()
{
return new[]
{
(byte)(this.PackedValue & 0xFF),
(byte)((this.PackedValue >> 8) & 0xFF),
(byte)((this.PackedValue >> 16) & 0xFF),
(byte)((this.PackedValue >> 24) & 0xFF)
};
}
I get the wrong results.
Here's a full code sample showing the expected and actual result.
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)
};
}
Upvotes: 5
Views: 159
Reputation: 15772
A much nicer solution to your problem is to using 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;
}
}
We create a new type that, instead of does bit shifting, gives you direct (type safe) access to the memory address that is inside the uint
.
Upvotes: 0
Reputation: 61512
The only reason it doesn't work for 1.5f
is because floats are not precise enough. Try 1.5d
(for double
) and your example will work. However this approach is limited to "nice" cases, i.e. those where the result in each byte is guaranteed to be a whole number. A special case is when you multiply by an integer, which will always work so long as none of the four results overflow.
It is also possible to do this for addition and subtraction provided that none of the individual bytes overflow. Obviously any overflow will mess up nearby bytes. This is particularly problematic if you wish to use 2's complement for negative bytes (-128 .. 127) because adding 3 to -2 is also an "overflow" and will mess up the next byte.
Upvotes: 5