C# 16 bit float conversions - c#

I am using the float 16 Half type from here -
https://gist.github.com/vermorel/1d5c0212752b3e611faf84771ad4ff0d
I have defined the following method to convert a Half value to binary string:
static string HalfToBinaryString(Half value)
{
int bitCount = Marshal.SizeOf(value) * 8;
string s = String.Empty;
// value is the value you want to convert to a string (double, long, int, ...)
foreach (byte b in Half.GetBytes(value))
{
s += Convert.ToString(b, 2).PadLeft(8, '0'); // for hex. For binary, use 2 and 8. For octal, use 8 and 3
}
return s;
}
And the following method to convert from binary string value to Half:
static Half HalfFromBinaryString(string bstra)
{
int intValue = Convert.ToInt16(bstra, 2);
return (Half)BitConverter.ToInt16(Half.GetBytes(intValue), 0);
}
When I perform the following division, I get:
string dividend = "11001001001111111";
Half result = (Half)(Convert.ToSingle(Convert.ToInt32(dividend, 2)) / 65536.0);
var rawbits = HalfToBinaryString(result);
//result = 1.571289
//rawbits = "0100100100111110"
However when I perform the reverse operation, I get:
Half halfval = HalfFromBinaryString(rawbits);
//halfval = 29840 //(instead of 1.571289)
How do I convert from a 16 bit binary string representation to the correct Half value (1.571289)?

Your implementation of HalfFromBinaryString is wrong, because your code is really just interpreting the string as an int in binary, and casting that int to a Half and returning. You could simplify your return line to return (Half)intValue; and it will still do the same thing.
Since the Half struct provides you with a ToHalf(ushort) method, you can use it to create a Half. Essentially, parse the string as a ushort, and pass the ushort into ToHalf:
ushort bits = Convert.ToUInt16(bstra, 2);
return Half.ToHalf(bits);
Your HalfToBinaryString method can also be simplified, as Half also provides the reverse operation of ToHalf - GetBits:
ushort bits = Half.GetBits(value);
return Convert.ToString(bits, 2).PadLeft(16, '0');

Related

How to load a huuuuuge string into a BigInteger in C# and not lose the ASCII encoding

I am using BigInteger.Parse(some string) but it takes forever and I'm not even sure if it finishes.
However, I can convert the huge string to a byte array and jam the byte array into a BigInteger constructor in very little time but it munges the original number stored in the string because of the endian issue with BigInteger and byte arrays.
Is there a way to convert the string to a byte array and put the byte array into the BigInteger object while preserving the original number stored in ASCII in the string?
String s = "12345"; // Some huge string, millions of digits.
BigInteger bi = new BigInteger(Encoding.ASCII.GetBytes(s); // very fast but the 12345 is lost
// OR...
BigInteger bi = BigInteger.Parse(s); // Takes forever therefore unuseable.
The byte[] representation of BigInteger has little to do with the ASCII characters. Much like the byte representation of an int has little to do with the ASCII representation of it.
To parse the number, each character must be converted to the digit value, and added to the previously parsed value multiplied by 10. That is probably why it's taking so long, and any version you write will probably not perform better. It has to do something like:
var nr=0;
foreach(var c in "123") nr=nr*10+(c-'0');
Edit
While it is not possible to perform the conversion by just converting to a byte array, the library implementation is slower then it has to be (at least for simple scenarios that do not need internationalization for example). Using the trick suggested by Rudy Velthuis in the comments and not taking into account hex formats or internationalization, I was able to produce a version which for 303104 characters runs ~5 times faster (from 18.2s to 3.75s. For 1 milion digits the fast method takes 47s, long, but it is a huge number):
public static class Helper
{
static BigInteger[] factors = Enumerable.Range(0, 19).Select(i=> BigInteger.Pow(10, i)).ToArray();
public static BigInteger ParseFast(string str)
{
var result = new BigInteger(0);
var n = str.Length;
var hasSgn = str[0] == '-';
int j;
for (var i = hasSgn ? 1 : 0; i < n; i += j - i)
{
long gr = 0;
for (j = i; j < i + 18 && j < n; j++)
{
gr = gr * 10 + (str[j] - '0');
}
result = result * factors[j-i]+ gr;
}
if (hasSgn)
{
result = BigInteger.MinusOne * result;
}
return result;
}
}

Byte/char buffer to long and/or double

In my code I need to convert string representation of integers to long and double values.
String representation is a byte array (byte[]). For example, for a number 12345 string representation is { 49, 50, 51, 52, 53 }
Currently, I use following obvious code for conversion to long (and almost the same code for conversion to double)
private long bytesToIntValue()
{
string s = System.Text.Encoding.GetEncoding("Latin1").GetString(bytes);
return long.Parse(s, CultureInfo.InvariantCulture);
}
This code works as expected, but in my case I want something better. It's because currently I must convert bytes to string first.
In my case, bytesToIntValue() gets called about 12 million times and about 25% of all memory allocations are made in this method.
Sure, I want to optimize this part. I want to perform conversions without intermediate string (+ speed, - allocation).
What would you recommend? How can I perform conversions without intermediate strings? Is there a faster method to perform conversions?
EDIT:
Byte arrays I am dealing with are always contain ASCII-encoded data. Numbers can be negative. For double values exponential format is allowed. Hexadecimal integers are not allowed.
How can I perform conversions without intermediate strings?
Well you can easily convert each byte to a char. For example - untested:
private static long ConvertAsciiBytesToInt32(byte[] bytes)
{
long value = 0;
foreach (byte b in bytes)
{
value *= 10L;
char c = b; // Implicit conversion; effectively ISO-8859-1
if (c < '0' || c > '9')
{
throw new ArgumentException("Bytes contains non-digit: " + c);
}
value += (c - '0');
}
return value;
}
Note that this really does assume it's ASCII (or compatible) - if your byte array is actually UTF-16 (for example) then it will definitely do the wrong thing.
Also note that this doesn't perform any sort of length validation or overflow checking... and it doesn't cope with negative numbers. You could add all of these if you want, but we don't know enough about your requirements to know if it's worth adding the complexity.
I'm not sure that there is a easy way to do that,
Please note that it won't work with other encodings, The test shown on my computer that this is only 3 times faster (I don't think it worth it).
The code + test :
class MainClass
{
public static void Main(string[] args)
{
string str = "12341234";
byte[] buffer = Encoding.ASCII.GetBytes(str);
Stopwatch sw = Stopwatch.StartNew();
for(int i = 0; i < 1000000 ;i ++)
{
long val = BufferToLong.GetValue(buffer);
}
Console.WriteLine (sw.ElapsedMilliseconds);
sw.Restart();
for (int i = 0 ; i < 1000000 ; i++)
{
string valStr = Encoding.ASCII.GetString(buffer);
long val = long.Parse(valStr);
}
Console.WriteLine (sw.ElapsedMilliseconds);
}
}
static class BufferToLong
{
public static long GetValue(Byte[] buffer) {
long number = 0;
foreach (byte currentByte in buffer) {
char currentChar = (char)currentByte;
int currentDigit = currentChar - '0';
number *= 10 ;
number += currentDigit;
}
return number;
}
}
In the end, I created C# version of strol function. This function comes with CRT and source code of CRT comes with Visual Studio.
The resulting method is almost the same as code provided by #Jon Skeet in his answer but also contains some checks for overflow.
In my case all the changes proved to be very useful in terms of speed and memory.

Using an int as the numerical representation of a string in C#

I'm trying to use an integer as the numerical representation of a string, for example, storing "ABCD" as 0x41424344. However, when it comes to output, I've got to convert the integer back into 4 ASCII characters. Right now, I'm using bit shifts and masking, as follows:
int value = 0x41424344;
string s = new string (
new char [] {
(char)(value >> 24),
(char)(value >> 16 & 0xFF),
(char)(value >> 8 & 0xFF),
(char)(value & 0xFF) });
Is there a cleaner way to do this? I've tried various casts, but the compiler, as expected, complained about it.
Characters are 16 bit, so you have to encode them into eight bit values to pack them in an integer. You can use the Encoding class to convert between characters and bytes, and the BitConverter class to convert between bytes and integer
Here is conversion both ways:
string original = "ABCD";
int number = BitConverter.ToInt32(Encoding.ASCII.GetBytes(original), 0);
string decoded = Encoding.ASCII.GetString(BitConverter.GetBytes(number));
Note that the order of the bytes in the integer depends on the endianess of the computer. On a little endian system the numeric value of "ABCD" will be 0x44434241. To get the reverse order, you can reverse the byte array:
byte[] data = Encoding.ASCII.GetBytes(original);
Array.Reverse(data);
int number = BitConverter.ToInt32(data, 0);
byte[] data2 = BitConverter.GetBytes(number);
Array.Reverse(data2);
string decoded = Encoding.ASCII.GetString(data2);
Or if you are using framework 3.5:
int number =
BitConverter.ToInt32(Encoding.ASCII.GetBytes(original).Reverse().ToArray() , 0);
string decoded =
Encoding.ASCII.GetString(BitConverter.GetBytes(number).Reverse().ToArray());
int value = 0x41424344;
string s = Encoding.ASCII.GetString(
BitConverter.GetBytes(value).Reverse().ToArray());
(The above assumes that you're running on a little-endian system. For big-endian you could just drop the .Reverse().ToArray() part, although if you are on a little-endian system then it would probably make more sense for you to just store "ABCD" as 0x44434241 in the first place, if possible.)
public string ConvertToHex(string asciiString)
{
string hex = "";
foreach (char c in asciiString)
{
int tmp = c;
hex += String.Format("{0:x2}", (uint)System.Convert.ToUInt32(tmp.ToString()));
}
return hex;
}
It will convert string to hex as you required.
public static string ToHexString(string value)
{
return value.Aggregate(new StringBuilder("0x"),
(sb, c) => sb.AppendFormat("{0:x2}", (int)c)).ToString();
}
if the string is never longer than 8 chars and a kind of Hexstring, you could use
the base variable 16 have a look at the Conversion functions from the Convert class.
string s = "ABCD";
uint i = Convert.ToUInt32( s, 16 );
MessageBox.Show( Convert.ToString( i, 16 ) );
regards
Oops

easy and fast way to convert an int to binary?

What I am looking for is something like PHPs decbin function in C#. That function converts decimals to its representation as a string.
For example, when using decbin(21) it returns 10101 as result.
I found this function which basically does what I want, but maybe there is a better / faster way?
var result = Convert.ToString(number, 2);
– Almost the only use for the (otherwise useless) Convert class.
Most ways will be better and faster than the function that you found. It's not a very good example on how to do the conversion.
The built in method Convert.ToString(num, base) is an obvious choice, but you can easily write a replacement if you need it to work differently.
This is a simple method where you can specify the length of the binary number:
public static string ToBin(int value, int len) {
return (len > 1 ? ToBin(value >> 1, len - 1) : null) + "01"[value & 1];
}
It uses recursion, the first part (before the +) calls itself to create the binary representation of the number except for the last digit, and the second part takes care of the last digit.
Example:
Console.WriteLine(ToBin(42, 8));
Output:
00101010
int toBase = 2;
string binary = Convert.ToString(21, toBase); // "10101"
To have the binary value in (at least) a specified number of digits, padded with zeroes:
string bin = Convert.ToString(1234, 2).PadLeft(16, '0');
The Convert.ToString does the conversion to a binary string.
The PadLeft adds zeroes to fill it up to 16 digits.
This is my answer:
static bool[] Dec2Bin(int value)
{
if (value == 0) return new[] { false };
var n = (int)(Math.Log(value) / Math.Log(2));
var a = new bool[n + 1];
for (var i = n; i >= 0; i--)
{
n = (int)Math.Pow(2, i);
if (n > value) continue;
a[i] = true;
value -= n;
}
Array.Reverse(a);
return a;
}
Using Pow instead of modulo and divide so i think it's faster way.

How can I Convert a Big decimal number to Hex in C# (Eg : 588063595292424954445828)

The number is bigger than int & long but can be accomodated in Decimal. But the normal ToString or Convert methods don't work on Decimal.
I believe this will produce the right results where it returns anything, but may reject valid integers. I dare say that can be worked around with a bit of effort though... (Oh, and it will also fail for negative numbers at the moment.)
static string ConvertToHex(decimal d)
{
int[] bits = decimal.GetBits(d);
if (bits[3] != 0) // Sign and exponent
{
throw new ArgumentException();
}
return string.Format("{0:x8}{1:x8}{2:x8}",
(uint)bits[2], (uint)bits[1], (uint)bits[0]);
}
Do it manually!
http://www.permadi.com/tutorial/numDecToHex/
I've got to agree with James - do it manually - but don't use base-16. Use base 2^32, and print 8 hex digits at a time.
I guess one option would be to keep taking chunks off it, and converting individual chunks? A bit of mod/division etc, converting individual fragments...
So: what hex value do you expect?
Here's two approaches... one uses the binary structure of decimal; one does it manually. In reality, you might want to have a test: if bits[3] is zero, do it the quick way, otherwise do it manually.
decimal d = 588063595292424954445828M;
int[] bits = decimal.GetBits(d);
if (bits[3] != 0) throw new InvalidOperationException("Only +ve integers supported!");
string s = Convert.ToString(bits[2], 16).PadLeft(8,'0') // high
+ Convert.ToString(bits[1], 16).PadLeft(8, '0') // middle
+ Convert.ToString(bits[0], 16).PadLeft(8, '0'); // low
Console.WriteLine(s);
/* or Jon's much tidier: string.Format("{0:x8}{1:x8}{2:x8}",
(uint)bits[2], (uint)bits[1], (uint)bits[0]); */
const decimal chunk = (decimal)(1 << 16);
StringBuilder sb = new StringBuilder();
while (d > 0)
{
int fragment = (int) (d % chunk);
sb.Insert(0, Convert.ToString(fragment, 16).PadLeft(4, '0'));
d -= fragment;
d /= chunk;
}
Console.WriteLine(sb);

Categories