Created
January 6, 2017 17:14
-
-
Save zsoi/a6c0429dd4247ec58fc3375c510a3dbd to your computer and use it in GitHub Desktop.
Utility classes to store floats or doubles in human-readable, yet exact representations. Inspired by http://the-witness.net/news/2011/12/engine-tech-concurrent-world-editing/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace ZS.Tools | |
{ | |
using System; | |
using System.Globalization; | |
using System.Text; | |
/// <summary> | |
/// The safe double class is mainly a interface between doubles and | |
/// their string representation. Traditional string representations of | |
/// doubles suffer from lossy precision. i.e. converting doubles to strings | |
/// to doubles to strings, ... over and over again changes the original value | |
/// of the double. The safe double class introduces some conversion methods which | |
/// will create/parse a special string format which contains a 1:1 representation | |
/// of the memory representation of the double and can thus be restored without the | |
/// fear of any data loss. | |
/// The "safe" format contains of two parts: A traditional human readable fixed double | |
/// notation and a machine-readable hex-encoded binary representation. These two are | |
/// separated by a colon. The machine-readable part is optional. When removing the colon and | |
/// the binary part, the humand readable notation will be parsed. If a binary part is | |
/// existing, the readable notation will be ignored (even if their values differ in a big dimension) | |
/// and the binary part will be restored. | |
/// </summary> | |
public static class SafeDouble | |
{ | |
/// <summary> | |
/// Converts a double to a safe representation that can be | |
/// restored without loss (i.e. bit precision) | |
/// </summary> | |
/// <returns>The double to convert</returns> | |
/// <param name="f">The string representation.</param> | |
public static string ToString(double f) | |
{ | |
byte[] bytes = BitConverter.GetBytes(f); | |
StringBuilder sb = new StringBuilder(); | |
sb.AppendFormat(CultureInfo.InvariantCulture, "{0:R} : ", f); | |
foreach (byte b in bytes) | |
{ | |
sb.AppendFormat("{0:x2}", b); | |
} | |
return sb.ToString(); | |
} | |
/// <summary> | |
/// Parses a string which should be in either fixed-point double notation "#.###"" | |
/// or in the combined safe double format "#.### : xxxxxxxx" | |
/// </summary> | |
/// <returns>The converted double</returns> | |
/// <param name="s">String in safe-double notation or in fixed double notation</param> | |
public static double ToDouble(string s) | |
{ | |
string[] parts = s.Replace(" ", "").Split(':'); | |
if (parts.Length == 2) | |
{ | |
byte[] bytes = new byte[8]; | |
for (int n = 0; n < bytes.Length; n++) | |
{ | |
bytes[n] = Convert.ToByte(parts[1].Substring(n * 2, 2), 16); | |
} | |
return BitConverter.ToDouble(bytes, 0); | |
} | |
else if (parts.Length == 1) | |
{ | |
return double.Parse(parts[0], CultureInfo.InvariantCulture); | |
} | |
throw new Exception("Safe double string has wrong format"); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace ZS.Tools | |
{ | |
using System; | |
using System.Globalization; | |
using System.Text; | |
/// <summary> | |
/// The safe float class is mainly a interface between floats and | |
/// their string representation. Traditional string representations of | |
/// floats suffer from lossy precision. i.e. converting floats to strings | |
/// to floats to strings, ... over and over again changes the original value | |
/// of the float. The safe float class introduces some conversion methods which | |
/// will create/parse a special string format which contains a 1:1 representation | |
/// of the memory representation of the float and can thus be restored without the | |
/// fear of any data loss. | |
/// The "safe" format contains of two parts: A traditional human readable fixed float | |
/// notation and a machine-readable hex-encoded binary representation. These two are | |
/// separated by a colon. The machine-readable part is optional. When removing the colon and | |
/// the binary part, the humand readable notation will be parsed. If a binary part is | |
/// existing, the readable notation will be ignored (even if their values differ in a big dimension) | |
/// and the binary part will be restored. | |
/// | |
/// Examples: | |
/// 1500 : 0080bb44 | |
/// 400 : 0000c843 | |
/// 20550 : 008ca046 | |
/// -0.2418448 : 27a677be | |
/// 0.664463 : 401a2a3f | |
/// </summary> | |
public static class SafeFloat | |
{ | |
/// <summary> | |
/// Converts a float to a safe representation that can be | |
/// restored without loss (i.e. bit precision) | |
/// </summary> | |
/// <returns>The float to convert</returns> | |
/// <param name="f">The string representation.</param> | |
public static string ToString(float f) | |
{ | |
byte[] bytes = BitConverter.GetBytes(f); | |
StringBuilder sb = new StringBuilder(); | |
sb.AppendFormat(CultureInfo.InvariantCulture, "{0:R} : ", f); | |
foreach (byte b in bytes) | |
{ | |
sb.AppendFormat("{0:x2}", b); | |
} | |
return sb.ToString(); | |
} | |
/// <summary> | |
/// Parses a string which should be in either fixed-point float notation "#.###"" | |
/// or in the combined safe float format "#.### : xxxxxxxx" | |
/// </summary> | |
/// <returns>The converted float</returns> | |
/// <param name="s">String in safe-float notation or in fixed float notation</param> | |
public static float ToFloat(string s) | |
{ | |
string[] parts = s.Replace(" ", "").Split(':'); | |
if (parts.Length == 2) | |
{ | |
byte[] bytes = new byte[4]; | |
for (int n = 0; n < bytes.Length; n++) | |
{ | |
bytes[n] = Convert.ToByte(parts[1].Substring(n * 2, 2), 16); | |
} | |
return BitConverter.ToSingle(bytes, 0); | |
} | |
else if (parts.Length == 1) | |
{ | |
return float.Parse(parts[0], CultureInfo.InvariantCulture); | |
} | |
throw new Exception("Safe float string has wrong format"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment