Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

way to avoid double conversion (byte[ ] to string to single) in .NET?

In a low latency HFT application I have sockets receiving Byte[] messages containing stock market data.

In the vendor protocol stock prices fields are Byte[] containing an ASCII character sequence.
I.e. byte sequence [51-51-46-56] corresponds to ascii characters [3-3-.-8], equals the 33.8 number.

Actually during message parsing I convert Byte[] to string and then string to Single/float.

Is there a way to avoid such double conversion and convert directly Byte[] to single/float ?
Secondly is there a way to avoid the use of the string type which in NET is inefficient (immutable-garbage collection prone) ?

Thanks in advance.

Regards

[edit jan 2019]: this is the final solution working flawlessly from 3 years:

/// <summary> Read a Double precision float field in GL format from the stream and advances stream position of n (1 of field lenght flag + field lenght). </summary>
/// <returns>-1 in case of error, Double precision floating point value ROUNDED TO 8 DECIMAL PLACES in case of success </returns>
/// <param name="IniPos"> Initial Stream Position</param>
public double ReadGLFieldAsDouble(long IniPos = -1)
{
// --
if (IniPos >= 0)
    Strm.Position = IniPos;
int FLenght = Strm.ReadByte - 32; // case of "01000" SW Snapshot, field 7 doesn't exists, but in the message is a blank (32)
                                  // --early exit
if (FLenght <= 0)
    return -1; // a blank field (byte 32) returns -1
if (Strm.Length - Strm.Position < FLenght)
    return -1;
// --
double Dbl = 0;
int Cpos = -1;
int b = 0;
int sign = 1;

// --Builds as Integer with no point Separator
for (int i = 0; i < FLenght ; i++)
{
    b = Strm.ReadByte;
    switch (b)
    {
        case 45: // ASCII 45="-" sign
            {
                sign = -1;
                break;
            }

        case 46: // ASCII 46 is decimal point="." ; ASCII 44 is comma=","
            {
                Cpos = i; // decimal point position
                break;
            }

        default:
            {
                Dbl = Dbl * 10 + (b - 48); // increments as integer ASCII 48=0
                break;
            }
    }
}

// --Transforms in floating point  dividing by power of 10, multiplies by sign and returns
if (Cpos != -1)
    Dbl = (Dbl / (Math.Pow(10, (FLenght - 1 - Cpos))));
return Math.Round(sign * Dbl, 8);
}
like image 423
Platypus Avatar asked Sep 16 '15 10:09

Platypus


2 Answers

 byte[] arr = new byte[] { 51, 51, 46, 56};
 double res = 0, floatindex = 0.1;
 bool isFraction = false;
 for (int i = 0; i < arr.Length; i++)
 {
    if (arr[i] == 46)
    {
        isFraction = true;
        continue;
    }

    if (!isFraction)
        res = 10*res + arr[i] - 48;
    else
    {
        res += (arr[i] - 48)*floatindex;
        floatindex /= 10.0;
    }
}
return res;

From a culture perspective, it assumes you always have a 'point' to mark the decimal separation (and not a coma like in French culture for instance)

like image 99
pierroz Avatar answered Oct 19 '22 20:10

pierroz


It is possible. Assuming you know the format is always like that (digits, comma and decimal digit):

float v = 0.1f* (100.0f * (buf[0] - 48) + 10 * (buf[1] - 48) + (buf[3] - 48));

or: float v = 0.1f * (100.0f * buf[0] + 10 * buf[1] + buf[3] - 4800 - 480 - 48);

like image 36
Marcin Cuprjak Avatar answered Oct 19 '22 19:10

Marcin Cuprjak