Greeting, I am having some trouble with saving a binary, I need to convert a float to a byte while keeping the bit pattern in tact (so I don't want to convert it to an integer). Anyone know how I'd go about doing this?
Yes, my blog is temporarily being repaired). use Code (csharp): BitConverter.GetBytes((float)inObject); and Code (csharp): BitConverter.ToSingle(inBytes, 0);
Maybe this could be useful Code (CSharp): using UnityEngine; using System.Collections.Generic; using System; public class UnitySerializer : MonoBehaviour{ private List<byte> byteStream = new List<byte>(); private byte[] byteArray; private int index = 0; /// <summary> /// Returns the stream as a Byte Array /// </summary> public byte[] ByteArray { get { if ( byteArray == null || byteStream.Count != byteArray.Length) byteArray = byteStream.ToArray(); return byteArray; } } /// <summary> /// Create a new empty stream /// </summary> public UnitySerializer() { } /// <summary> /// Initialiaze a stream from a byte array. /// Used for deserilaizing a byte array /// </summary> /// <param name="ByteArray"></param> public UnitySerializer(byte[] ByteArray) { byteArray = ByteArray; byteStream = new List<byte>(ByteArray); } // --- double --- public void Serialize(double d) { byteStream.AddRange( BitConverter.GetBytes(d)); } public double DeserializeDouble() { double d = BitConverter.ToDouble(ByteArray, index); index += 8; return d; } // // --- bool --- public void Serialize(bool b) { byteStream.AddRange(BitConverter.GetBytes(b)); } public bool DeserializeBool() { bool b = BitConverter.ToBoolean(ByteArray, index); index += 1; return b; } // // --- Vector2 --- public void Serialize(Vector2 v) { byteStream.AddRange(GetBytes(v)); } public Vector2 DeserializeVector2() { Vector2 vector2 = new Vector2(); vector2.x = BitConverter.ToSingle(ByteArray, index); index += 4; vector2.y = BitConverter.ToSingle(ByteArray, index); index += 4; return vector2; } // // --- Vector3 --- public void Serialize(Vector3 v) { byteStream.AddRange(GetBytes(v)); } public Vector3 DeserializeVector3() { Vector3 vector3 = new Vector3(); vector3.x = BitConverter.ToSingle(ByteArray, index); index += 4; vector3.y = BitConverter.ToSingle(ByteArray, index); index += 4; vector3.z = BitConverter.ToSingle(ByteArray, index); index += 4; return vector3; } // // --- Type --- public void Serialize(System.Type t) { // serialize type as string string typeStr = t.ToString(); Serialize(typeStr); } public Type DeserializeType() { // type stored as string string typeStr = DeserializeString(); return Type.GetType(typeStr); ; } // // --- String --- public void Serialize(string s) { // add the length as a header byteStream.AddRange(BitConverter.GetBytes(s.Length)); foreach (char c in s) byteStream.Add((byte)c); } public string DeserializeString() { int length = BitConverter.ToInt32(ByteArray, index); index += 4; string s = ""; for (int i = 0; i < length; i++) { s += (char)ByteArray[index]; index++; } return s; } // // --- byte[] --- public void Serialize(byte[] b) { // add the length as a header byteStream.AddRange(BitConverter.GetBytes(b.Length)); byteStream.AddRange(b); } public byte[] DeserializeByteArray() { int length = BitConverter.ToInt32(ByteArray, index); index += 4; byte[] bytes = new byte[length]; for (int i = 0; i < length; i++) { bytes[i] = ByteArray[index]; index++; } return bytes; } // // --- Quaternion --- public void Serialize(Quaternion q) { byteStream.AddRange(GetBytes(q)); } public Quaternion DeserializeQuaternion() { Quaternion quat = new Quaternion(); quat.x = BitConverter.ToSingle(ByteArray, index); index += 4; quat.y = BitConverter.ToSingle(ByteArray, index); index += 4; quat.z = BitConverter.ToSingle(ByteArray, index); index += 4; quat.w = BitConverter.ToSingle(ByteArray, index); index += 4; return quat; } // // --- float --- public void Serialize(float f) { byteStream.AddRange(BitConverter.GetBytes(f)); } public float DeserializeFloat() { float f = BitConverter.ToSingle(ByteArray, index); index += 4; return f; } // // --- int --- public void Serialize(int i) { byteStream.AddRange(BitConverter.GetBytes(i)); } public int DeserializeInt() { int i = BitConverter.ToInt32(ByteArray, index); index += 4; return i; } // // --- internal ---- Vector3 DeserializeVector3(byte[] bytes, ref int index) { Vector3 vector3 = new Vector3(); vector3.x = BitConverter.ToSingle(bytes, index); index += 4; vector3.y = BitConverter.ToSingle(bytes, index); index += 4; vector3.z = BitConverter.ToSingle(bytes, index); index += 4; return vector3; } Quaternion DeserializeQuaternion(byte[] bytes, ref int index) { Quaternion quat = new Quaternion(); quat.x = BitConverter.ToSingle(bytes, index); index += 4; quat.y = BitConverter.ToSingle(bytes, index); index += 4; quat.z = BitConverter.ToSingle(bytes, index); index += 4; quat.w = BitConverter.ToSingle(bytes, index); index += 4; return quat; } byte[] GetBytes(Vector2 v) { List<byte> bytes = new List<byte>(8); bytes.AddRange(BitConverter.GetBytes(v.x)); bytes.AddRange(BitConverter.GetBytes(v.y)); return bytes.ToArray(); } byte[] GetBytes(Vector3 v) { List<byte> bytes = new List<byte>(12); bytes.AddRange(BitConverter.GetBytes(v.x)); bytes.AddRange(BitConverter.GetBytes(v.y)); bytes.AddRange(BitConverter.GetBytes(v.z)); return bytes.ToArray(); } byte[] GetBytes(Quaternion q) { List<byte> bytes = new List<byte>(16); bytes.AddRange(BitConverter.GetBytes(q.x)); bytes.AddRange(BitConverter.GetBytes(q.y)); bytes.AddRange(BitConverter.GetBytes(q.z)); bytes.AddRange(BitConverter.GetBytes(q.w)); return bytes.ToArray(); } // public static void Example() // { // // // Debug.Log("--- UnitySerializer Example ---"); // Vector2 point = UnityEngine.Random.insideUnitCircle; // Vector3 position = UnityEngine.Random.onUnitSphere; // Quaternion quaternion = UnityEngine.Random.rotation; // float f = UnityEngine.Random.value; // int i = UnityEngine.Random.Range(0, 10000); // double d = (double)UnityEngine.Random.Range(0, 10000); // string s = "Brundle Fly"; // bool b = UnityEngine.Random.value < 0.5f ? true : false; // System.Type type = typeof(UnitySerializer); // // // // Debug.Log("--- Before ---"); // Debug.Log(point + " " + position + " " + quaternion + " " + f + " " + d + " " + s + " " + b + " " + type); // // // // Debug.Log("--- Serialize ---"); // UnitySerializer us = new UnitySerializer(); // us.Serialize(point); // us.Serialize(position); // us.Serialize(quaternion); // us.Serialize(f); // us.Serialize(i); // us.Serialize(d); // us.Serialize(s); // us.Serialize(b); // us.Serialize(type); // byte[] byteArray = us.ByteArray; // // // the array must be deserialized in the same order as it was serialized // Debug.Log("--- Deserialize ---"); // UnitySerializer uds = new UnitySerializer(byteArray); // Vector2 point2 = uds.DeserializeVector2(); // Vector3 position2 = uds.DeserializeVector3(); // Quaternion quaternion2 = uds.DeserializeQuaternion(); // float f2 = uds.DeserializeFloat(); // int i2 = uds.DeserializeInt(); // double d2 = uds.DeserializeDouble(); // string s2 = uds.DeserializeString(); // bool b2 = uds.DeserializeBool(); // System.Type type2 = uds.DeserializeType(); // // // // Debug.Log("--- After ---"); // Debug.Log(point2 + " " + position2 + " " + quaternion2 + " " + f2 + " " + d2 + " " + s2 + " " + b2 + " " + type2); // } }
I'm sorry, but could you please put that in simpler terms? And tbe paragraph didn't mention endian 'safe'.
It's a complex topic, and not really easy to ELI5. You have to understand at least the basics of storing data as bits. The consequences of not being endian safe is that data saved on one computer might not be possible to load on a different computer. So if you're making save games for a local thing, it's fine, but if you're making something like cloud saves, it's not a good idea. very simplified: "big endian" and "small endian" boils down to if the bits of your data is stored left-to-right or right-to-left. For a byte, that's the difference between: 4 = 00000100 <- big endian, biggest bit stored first and 4 = 00100000 <- small endian, biggest bit stored last So if you have a small-endian 4 that's read of as if it was big-endian, you'd get 64 instead.
Endian is byte-order, not bit-order. e.g., a 16-bit big-endian number stored as two bytes has the "largest" (most significant) byte first, followed by the least significant byte. Little-endian is the opposite. Nothing to do with bits; 00000100 is always 4. --Eric
Thanks for the help, so if i have a float, but want to send it as a ushort over the network, then convert it back to a float on the other players machine, i will run into problems?
Maybe, if you didn't use network byte order and the other machine's CPU had a different endianness than yours (not likely these days, even most consoles are x86-64 instead of PPC now). --Eric
It's only the matter of a single line of code to check the endianness of the current machine's CPU (BitConverter.isLittleEndian) and know that you have to flip it. Worth noting that endianness is just a factor of integral types (int16, int32, int64) and not (I believe) a factor in floating point values. If you convert the float to ushort first, then to bytes, then transfer that, then put it back together into a ushort, then convert to float again, then endianness may matter and you should check the architecture when loading to ensure it's deserialized in the proper order. If it's not an integral type, it shouldn't matter.
I can account for the byte conversion, not for the network protocol. I have no idea what transfer method you're using, so I don't know if it automatically accounts for differences in architecture for known integral types. I would assume it would, so if you transfer a ushort on one end, it should be read property (in the right order) as a ushort on the other side. Conversion to byte arrays mean that the transfer protocol can't account for what data needs to be read one way or another. You could be transferring an integral type or not, who knows? So you have to handle flipping the array yourself, as appropriate. That's my limited understanding of the situation- I've never made a network game, so don't take it as gospel. If someone else could confirm this or correct me, that would be great.
I wouldn't be using byte arrays, just a single byte or ushort. Dang, all of this confusing stuff makes me feel like I've not even scraped the surface of programming languages.
Anything longer than a byte could be misread if you send it to another computer. It's up to you to make sure it arrives (and works) as intended
float f; BinaryWriter writer = new BinaryWriter(File.Open(fileName, FileMode.Create)); writer.Write(f); writer.Close(); byte[] bytes = File.ReadAllBytes(fileName); // bytes will have byte [] representation of your float f
Did you really just necro a post to show code that uses disk IO to convert a float to it's byte representation? A solution that's also not endian safe, just like the 8 year old original answer which doesn't use disk IO. If so... the only good reason I could see to necro a post is to exercise your burgeoning programming skills. In which case some notes to take about your solution. You should avoid writing to a storage medium unless you're attempting to actually store something long term, or work with data sets significantly larger than working memory. Since you're not storing anything, and a float is a mere 4 bytes of memory, there's no reason to get the storage IO involved. It's probably the tightest bottleneck in your entire system. What you've effectively done is read a float from RAM, into the CPU, did some work on it, sent that result down the IO backplane (may that be over the pci-e bus, or worst through the southbridge) and onto a storage device. Then read that same data back up through the IO backplane likely through either the CPU or southbridge and back into RAM. Rather than... RAM -> CPU -> RAM which is probably the fastest pipe in your entire system (outside of the CPU itself, or the internals of your graphics card).