Using Pass by value or Pass by Reference? - c#

I have two methods in my code. Below is one of them.
private async void Characteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
{
var newValue = FormatValueByPresentation(args.CharacteristicValue, presentationFormat);
var message = $"Value at {DateTime.Now:hh:mm:ss.FFF}: {newValue}";
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() => CharacteristicLatestValue.Text = message);
}
And it's printing time (Value At) like this.
Now, this is the second method.
private static ushort ParseHeartRateValue(byte[] data)
{
const byte heartRateValueFormat = 0x04;
byte flags = data[0];
ushort offset = 1;
bool HRC2 = (flags & 0x80) > 0;
if (HRC2) //if BPM is un uint16
{
short hr = BitConverter.ToInt16(data, offset);
offset += 2;
System.Diagnostics.Debug.WriteLine("We have 16:" + hr.ToString("x"));
}
else // if BPM is uint8
{
byte hr = data[offset];
offset += 1;
System.Diagnostics.Debug.WriteLine("no 16:" + hr.ToString("x"));
}
bool ee = (flags & (1 << 3)) != 0;
if (ee)
offset += 2;
// bool rr = ((flags & 1 << 4) != 0);
bool rr = ((flags & 0x10) != 0);
if (rr)
{
int count = (data.Length - offset) / 2;
for (int i = 0; i < count; i++)
{
ushort value = BitConverter.ToUInt16(data, offset);
intervals.Add((double)value); // Added
if (intervals.Count > 190) // Added
intervals.RemoveAt(0);// Added
double mean = intervals.Average();// Added
double sumOfSquareDiff = intervals.Select(val => (val - mean) * (val - mean)).Sum(); // Added
double vrHR = Math.Sqrt(sumOfSquareDiff / intervals.Count); // Added
double intervalLengthInSeconds = value / 1024.0;
offset += 2;
System.Diagnostics.Debug.WriteLine("Heart Rate Variability:" + vrHR.ToString());
}
}
And it's printing the output like this.
But I want the Heart Rate Variability to print just below "Value at".
How do I make that work ?
Should I do pass by value or reference? Any other suggestions ?
I asked more detailed question earlier here on Stack Overflow

But I want the Heart Rate Variability to print just below "Value at". How do I make that work ?
Your question completely was unrelated to 'pass by value or reference'. The CharacteristicLatestValue just is a TextBlock control on XAML page. It's used to show text on UI like the following:
Value at 01:11:25:453: Heart Rate: 124
If you want to show 'no 16:51', 'Heart Rate Varibility:661841865028902' etc these texts blow it like the following:
Value at 01:11:25:453: Heart Rate: 124
no 16:51
Heart Rate Varibility:661841865028902
You just need to add them after the CharacteristicLatestValue.Text like the following:
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() => CharacteristicLatestValue.Text = "Value at 01:11:25:453: Heart Rate: 124"+"\r\n"+ "no 16:51"+"\r\n"+ "Heart Rate Varibility:661841865028902"+"\r\n");

Related

C# floating point to binary string and vice versa

I am converting a floating point value to binary string representation:
float resulta = 31.0 / 15.0; //2.0666666
var rawbitsa = ToBinaryString(resulta); //returns 01000000000001000100010001000100
where ToBinaryString is coded as:
static string ToBinaryString(float value)
{
int bitCount = sizeof(float) * 8; // never rely on your knowledge of the size
// better not use string, to avoid ineffective string concatenation repeated in a loop
char[] result = new char[bitCount];
// now, most important thing: (int)value would be "semantic" cast of the same
// mathematical value (with possible rounding), something we don't want; so:
int intValue = System.BitConverter.ToInt32(BitConverter.GetBytes(value), 0);
for (int bit = 0; bit < bitCount; ++bit)
{
int maskedValue = intValue & (1 << bit); // this is how shift and mask is done.
if (maskedValue > 0)
maskedValue = 1;
// at this point, masked value is either int 0 or 1
result[bitCount - bit - 1] = maskedValue.ToString()[0];
}
return new string(result); // string from character array
}
Now I want to convert this binary string to float value.
I tried the following but it returns value "2.8293250329111622E-315"
string bstra = "01000000000001000100010001000100";
long w = 0;
for (int i = bstra.Length - 1; i >= 0; i--) w = (w << 1) + (bstra[i] - '0');
double da = BitConverter.ToDouble(BitConverter.GetBytes(w), 0); //returns 2.8293250329111622E-315
I want the value "2.0666666" by passing in value "01000000000001000100010001000100"
Why am I getting a wrong value? Am I missing something?
You're making this a lot harder than it needs to be; the error seems to be mostly in the character parsing code, but you don't need to do all that.
You could try like this instead:
static string ToBinaryString(float value)
{
const int bitCount = sizeof(float) * 8;
int intValue = System.BitConverter.ToInt32(BitConverter.GetBytes(value), 0);
return Convert.ToString(intValue, 2).PadLeft(bitCount, '0');
}
static float FromBinaryString(string bstra)
{
int intValue = Convert.ToInt32(bstra, 2);
return BitConverter.ToSingle(BitConverter.GetBytes(intValue), 0);
}
Example:
float resulta = 31.0F / 15.0F; //2.0666666
var rawbitsa = ToBinaryString(resulta);
Console.WriteLine(rawbitsa); //01000000000001000100010001000100
var back = FromBinaryString(rawbitsa);
Console.WriteLine(back); //2.0666666
Note that the usage of GetBytes is kinda inefficient; if you're OK with unsafe code, you can remove all of that.
Also note that this code is CPU-specific - it depends on the endianness.

Convert and update continuous serial port communication from Arduino to an integer array in Visual Studios (C#)

I am currently reading data coming in continuously from an i2c sensor connected to an Arduino and capturing the data successfully in Visual Studios. The sensor is getting 4 different X, Y values and then sends them via Serial.Print in the format of:
X1,Y1,X2,Y2,X3,Y3,X4,Y4 (Where X1 - X4 and Y1 - Y4 are values that range betwen 0-1023 i.e., 1023,1023,1023,1023,1023,1023,1023,1023) which is then followed up with a Serial.println("") command. This whole process is repeated continuously until turned off. Arduino code:
for (i=0;i<16;i++) { data_buf[i]=0; }
i=0;
while(Wire.available() && i < 16) {
data_buf[i] = Wire.read();
i++;
}
Ix[0] = data_buf[1];
Iy[0] = data_buf[2];
s = data_buf[3];
//Ix[0] += (s & 0x30) <<4;
Ix[0] = data_buf[1] | ((data_buf[3] >> 4) & 0x03) << 8;
Iy[0] = data_buf[2] | ((data_buf[3] >> 6) & 0x03) << 8;
//Ix[0] = Ix[0] / test;
Ix[1] = data_buf[4];
Iy[1] = data_buf[5];
s = data_buf[6];
Ix[1] += (s & 0x30) <<4;
Iy[1] += (s & 0xC0) <<2;
Ix[2] = data_buf[7];
Iy[2] = data_buf[8];
s = data_buf[9];
Ix[2] += (s & 0x30) <<4;
Iy[2] += (s & 0xC0) <<2;
Ix[3] = data_buf[10];
Iy[3] = data_buf[11];
s = data_buf[12];
Ix[3] += (s & 0x30) <<4;
Iy[3] += (s & 0xC0) <<2;
for(i=0; i<4; i++)
{
if (Ix[i] < 1000)
Serial.print("");
if (Ix[i] < 100)
Serial.print("");
if (Ix[i] < 10)
Serial.print("");
Serial.print( int(Ix[i]) );
Serial.print(",");
if (Iy[i] < 1000)
Serial.print("");
if (Iy[i] < 100)
Serial.print("");
if (Iy[i] < 10)
Serial.print("");
Serial.print( int(Iy[i]) );
if (i<3)
Serial.print(",");
}
Serial.println("");
delay(15);
}
What I'm have problems with, is converting those values from within Visual Studios so that I can pass on the data as individual integer values. What I have now:
void _spManager_NewSerialDataRecieved(object sender, SerialDataEventArgs e)
{
if (this.InvokeRequired)
{
// Using this.Invoke causes deadlock when closing serial port, and BeginInvoke is good practice anyway.
this.BeginInvoke(new EventHandler<SerialDataEventArgs>(_spManager_NewSerialDataRecieved), new object[] { sender, e });
return;
}
int maxTextLength = 1000; // maximum text length in text box
if (tbData.TextLength > maxTextLength)
tbData.Text = tbData.Text.Remove(0, tbData.TextLength - maxTextLength);
// This application is connected to a Arduino sending ASCCI characters, so data is converted to text
string Rawstr = Encoding.ASCII.GetString(e.Data);
//Get values that are between the ',' from e.Data and store them in our new array
string[] str = Rawstr.Split(',');
//Convert str array into new int[] array (or similar) I've tried multiple things with no success
??????
//This results in a "Input string was not in a correct format" error.
int xx = Convert.ToInt32(str[0]);
tbData.AppendText(Rawstr);
tbData.ScrollToCaret();
}
This is where I am currently stuck. I believe I have an issue where pretty much an infant number of values are going into the string[] str because I don't have a limit on it before it starts to put new values into/and replacing the string values already in it.
Any guidance is appreciated.
You can use Linq.
First split on ,, skip the empty and whitespace characters, then trim of the first character (X or Y) and convert the remaining part to an integer.
String Rawstr = "x1,y1,x2,y2," + Environment.NewLine;
Int32[] integers = Rawstr.Split(new [] { ',' }, StringSplitOptions.None).Where(s => !String.IsNullOrWhiteSpace(s)).Select(s => Convert.ToInt32(s.Substring(1))).ToArray();
For "x1,y1,x2,y2," you get an integer array containing [1,1,2,2].

Match a sequence of bits in a number and then convert the match into zeroes?

My assignment is to search through the binary representation of a number and replace a matched pattern of another binary representation of a number. If I get a match, I convert the matching bits from the first integer into zeroes and move on.
For example the number 469 would be 111010101 and I have to match it with 5 (101). Here's the program I've written so far. Doesn't work as expected.
using System;
namespace Conductors
{
class Program
{
static void Main(string[] args)
{
//this is the number I'm searching for a match in
int binaryTicket = 469;
//This is the pattern I'm trying to match (101)
int binaryPerforator = 5;
string binaryTicket01 = Convert.ToString(binaryTicket, 2);
bool match = true;
//in a 32 bit integer, position 29 is the last one I would
//search in, since I'm searching for the next 3
for (int pos = 0; pos < 29; pos++)
{
for (int j = 0; j <= 3; j++)
{
var posInBinaryTicket = pos + j;
var posInPerforator = j;
int bitInBinaryTicket = (binaryTicket & (1 << posInBinaryTicket)) >> posInBinaryTicket;
int bitInPerforator = (binaryPerforator & (1 << posInPerforator)) >> posInPerforator;
if (bitInBinaryTicket != bitInPerforator)
{
match = false;
break;
}
else
{
//what would be the proper bitwise operator here?
bitInBinaryTicket = 0;
}
}
Console.WriteLine(binaryTicket01);
}
}
}
}
Few things:
Use uint for this. Makes things a hell of a lot easier when dealing with binary numbers.
You aren't really setting anything - you're simply storing information, which is why you're printing out the same number so often.
You should loop the x times where x = length of the binary string (not just 29). There's no need for inner loops
static void Main(string[] args)
{
//this is the number I'm searching for a match in
uint binaryTicket = 469;
//This is the pattern I'm trying to match (101)
uint binaryPerforator = 5;
var numBinaryDigits = Math.Ceiling(Math.Log(binaryTicket, 2));
for (var i = 0; i < numBinaryDigits; i++)
{
var perforatorShifted = binaryPerforator << i;
//We need to mask off the result (otherwise we fail for checking 101 -> 111)
//The mask will put 1s in each place the perforator is checking.
var perforDigits = (int)Math.Ceiling(Math.Log(perforatorShifted, 2));
uint mask = (uint)Math.Pow(2, perforDigits) - 1;
Console.WriteLine("Ticket:\t" + GetBinary(binaryTicket));
Console.WriteLine("Perfor:\t" + GetBinary(perforatorShifted));
Console.WriteLine("Mask :\t" + GetBinary(mask));
if ((binaryTicket & mask) == perforatorShifted)
{
Console.WriteLine("Match.");
//Imagine we have the case:
//Ticket:
//111010101
//Perforator:
//000000101
//Is a match. What binary operation can we do to 0-out the final 101?
//We need to AND it with
//111111010
//To get that value, we need to invert the perforatorShifted
//000000101
//XOR
//111111111
//EQUALS
//111111010
//Which would yield:
//111010101
//AND
//111110000
//Equals
//111010000
var flipped = perforatorShifted ^ ((uint)0xFFFFFFFF);
binaryTicket = binaryTicket & flipped;
}
}
string binaryTicket01 = Convert.ToString(binaryTicket, 2);
Console.WriteLine(binaryTicket01);
}
static string GetBinary(uint v)
{
return Convert.ToString(v, 2).PadLeft(32, '0');
}
Please read over the above code - if there's anything you don't understand, leave me a comment and I can run through it with you.

CPU High Usage with time for serial port

Iam currently working on a project where i have to read serial port continuously. The data is coming continuously for a max of 45 min. I have to validate the data via checksum and create a packet of 79 bytes. After this i have to plot the data(Trajectory) on a real time basis. The problem with the code is that at start it uses 20% of the CPU usage(Pentium 4, 3.0 GHz, Hyper threading)(which i think is still high) but with time the CPU usage increases and at end it reaches to 60%.
The data is coming in with a baud rate of 115200 and is sent continuously at the rate 100 msec.
My code for reading, validating and plotting is as follows:
The following function receive the data and validate it...
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
header1 = serialPort1.ReadByte();
if (header1 == 0)
header2 = serialPort1.ReadByte();
if ((header1 == 0) && (header2 == 1))//Store the data in an array.
{
for (int i = 0; i < 77; i++)
abudata[i] = serialPort1.ReadByte();
tail = abudata[76];
}
else
{
serialPort1.DiscardInBuffer();
}
checksum = 1;// Calculate the checksum.
for (i = 0; i < 74; i++)
checksum = checksum + (abudata[i]);
checksum1 = (abudata[75] << 8);
checksum1 = checksum1 + (abudata[74]);
if ((checksum == checksum1) && (tail == 4))
this.Invoke(new EventHandler(Display_Results));// Function to display
}
catch (Exception ode)
{
l4[4].BackColor = Color.Red;
}
}
The following function display the data on labels and draw the trajectory on a picture box
private void Display_Results(object s, EventArgs e)
{
head1[0] = header1;
head1[1] = header2;
for (k = 0; k < 77; ++k)
head1[k + 2] = (((int)abudata[k]) & 0x000000ff);
jk = 0;
for (k = 0; k < 36; ++k) //Data packing into 36 bytes
{
num_1[k] = (ulong)((head1[jk + 1]) + (head1[jk] << 8)) & 0x0000ffff;
num_1[k] = (double)num_1[k];
num_2[k] = (double)num_1[k];
jk = jk + 2;
signbit = (int)num_1[k] >> 15;
if (signbit == 1)
{
sgnval = -1;
num_1[k] = num_1[k] - 65535;
num_1[k] = num_1[k] * (-1.0);
}
else
sgnval = 1;
//Converting the data into engineering values
engval[k] = Math.Round(num_1[k] * parammaxval[k] * sgnval / 32767.0, 3);
if (k == 14)
{
try
{
curr_x = (pictureBox2.Width / 2) + (int)((engval[13] * (pictureBox2.Width)) / map_width);
curr_y = (pictureBox2.Height / 2) - (int)((engval[14] * (pictureBox2.Height)) / map_height);
PointF p1 = new Point(curr_x, curr_y);
if (_gPath != null && _gPath.PointCount > 0)
p1 = _gPath.PathPoints[_gPath.PathPoints.Length - 1];
PointF p2 = new Point(curr_x, curr_y);
_gPath.AddLine(p1, p2);
pictureBox2.Invalidate();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
I extensively work with serial port connected devices and may assure you, that regular using of SerialPort class by itself doesn't produce high CPU load.
FIRST I would suggest you to PROFILE your application. There are a bunch of profilers for .NET
Only after profiling I would suggest to decouple SerialPort reading and data processing. Use Producer Consumer pattern. Put data from SerialPort to queue and consume it from other thread.
That what I have in SerialPortDataReceived function in one of my projects
private void SerialPortDataReceived(object sender, SerialDataReceivedEventArgs e)
{
lock (SyncObject)
{
if (!_serialPort.IsOpen) return;
try
{
int toread = _serialPort.BytesToRead;
byte[] bytes = new byte[toread];
_serialPort.Read(bytes, 0, toread);
ProducerAddBytes(bytes);
}
catch (TimeOutException)
{
//logic
}
}
}
P.S. But PROFILE FIRST!
i got what was the problem with the above code..
I am using Graph path for plotting the trajectory
if (k == 14)
{
try
{
curr_x = (pictureBox2.Width / 2) + (int)((engval[13] * (pictureBox2.Width)) / map_width);
curr_y = (pictureBox2.Height / 2) - (int)((engval[14] * (pictureBox2.Height)) / map_height);
PointF p1 = new Point(curr_x, curr_y);
if (_gPath != null && _gPath.PointCount > 0)
p1 = _gPath.PathPoints[_gPath.PathPoints.Length - 1];
PointF p2 = new Point(curr_x, curr_y);
_gPath.AddLine(p1, p2);
pictureBox2.Invalidate();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Now as the application continues to run, it gathers lots of graph points and hence plotting this huge number of point is consuming the resources.
Can anybody suggest me solution to this problem that how to plot the trajectory without slowing the system...

Obscure VBMath random numbers generator behavior

I want to repeat a random number sequence generated by a legacy software using the VBMath.Rnd and VBMath.Randomize functions in VB .NET
Reading on the documentation for those functions on MSDN i found that you are supposed to "reset" the generator calling Rnd with a negative value if you want the same seed to give you the same result sequence each time.
But doing some tests... things didn't work as expected.
The legacy software does something like this at the start of the application on different executions:
float[] rNums = new float[4];
VBMath.Randomize(154341.77394338892);
for (int index = 0; index < 4; index++)
{
rNums[index] = VBMath.Rnd();
}
And my code does something like this:
VBMath.Rnd(-1);
VBMath.Randomize(154341.77394338892);
for (int index = 0; index < 4; index++)
{
Console.WriteLine("rNum[" + index + "] " + rNums[index] + " = " + VBMath.Rnd());
}
The results for this test are:
rNum[0] 0,6918146 = 0,2605162
rNum[1] 0,5121228 = 0,4748411
rNum[2] 0,8309224 = 0,8112976
rNum[3] 0,972851 = 0,8011347
The sequence that i want to reproduce in the second code any number of times is the sequence generated from the hard coded initial state of the generator. That means the sequence you would get if you run the first code alone.
I can not change the first code.
Any idea on why the VBMath.Rnd and VBMath.Randomize functions arent working as expected?
Did i miss something?
ANSWER
Thee problem is that since the legacy code doesn't call Rnd with a negative value, the generator doesn't clear its state and the call to Rnd gets chained to the previous value of the seed (in this case, the hard-coded value).
To solve the problem and be able to repeat the process all over again without all the problems that would imply "reproducing" the initial state, i cloned the generator code and patched it so i could reproduce the same situation every time depending on a parameter.
I know.. its ugly.. but it solves my problem (Btw i also know that there are some rounding errors and that the generated values are not exact.. they differ in like the last digit or something) but i don't need exact precision.
The rounding error probably comes from my choice of language for the cloning of the algorithm. If someone could help out on how to get the exact same result (match the rounding errors) that would be nice.
The patched code follows.
public sealed class RndGenerator
{
static int m_rndSeed = 0x50000;
// This is the value that the programmer sets the seed at ProjectData object
// initialization
const int CONSTANT_INIT_RNDSEED = 0x50000;
// Methods
private static float GetTimer()
{
DateTime now = DateTime.Now;
return (float)(((((60 * now.Hour) + now.Minute) * 60) + now.Second) + (((double)now.Millisecond) / 1000.0));
}
public static void Randomize()
{
float timer = GetTimer();
int rndSeed = m_rndSeed;
int num = BitConverter.ToInt32(BitConverter.GetBytes(timer), 0);
num = ((num & 0xffff) ^ (num >> 0x10)) << 8;
rndSeed = (rndSeed & -16776961) | num;
m_rndSeed = rndSeed;
}
public static void Randomize(double Number)
{
Randomize(Number, false);
}
public static void Randomize(double Number, bool useHardCodedState)
{
int num;
int rndSeed = 0;
if (useHardCodedState)
rndSeed = CONSTANT_INIT_RNDSEED;
else
rndSeed = m_rndSeed;
if (BitConverter.IsLittleEndian)
{
num = BitConverter.ToInt32(BitConverter.GetBytes(Number), 4);
}
else
{
num = BitConverter.ToInt32(BitConverter.GetBytes(Number), 0);
}
num = ((num & 0xffff) ^ (num >> 0x10)) << 8;
rndSeed = (rndSeed & -16776961) | num;
m_rndSeed = rndSeed;
}
public static float Rnd()
{
return Rnd(1f);
}
public static float Rnd(float Number)
{
int rndSeed = m_rndSeed;
if (Number != 0.0)
{
if (Number < 0.0)
{
long num3 = BitConverter.ToInt32(BitConverter.GetBytes(Number), 0);
num3 &= (long)0xffffffffL;
rndSeed = (int)((num3 + (num3 >> 0x18)) & 0xffffffL);
}
rndSeed = (int)(((rndSeed * 0x43fd43fdL) + 0xc39ec3L) & 0xffffffL);
}
m_rndSeed = rndSeed;
return (((float)rndSeed) / 1.677722E+07f);
}
}
MSDN says:
To repeat sequences of random numbers, call Rnd with a negative argument immediately before using Randomize with a numeric argument. Using Randomize with the same value for Number does not repeat the previous sequence.
Only one of the code samples you show calls Rnd with a negative argument immediately before using Randomize with a numeric argument.
If code B has a call to Rnd(-1), it should generate the same sequence on all runs. If the sequence generated by a run of code B ( with Rnd(-1) ) repeated that generated by a run of code A ( without Rnd(-1) ), then the different runs of code A would have have to generate the same sequence. This contradicts the information in MSDN.
The second set of code works as expected and will repeatedly give you the same set of 4 numbers.
The first set does not, because it lacks the Rnd(-1) entry. As MSDN says :
Using Randomize with the same value for Number does not repeat the previous sequence
Running the first set 3 times in a row gives this :
rNum[0] 0 = 0.6918146
rNum[1] 0 = 0.5121228
rNum[2] 0 = 0.8309224
rNum[3] 0 = 0.972851
rNum[0] 0 = 0.5982737
rNum[1] 0 = 0.323263
rNum[2] 0 = 0.05594879
rNum[3] 0 = 0.5724301
rNum[0] 0 = 0.5555484
rNum[1] 0 = 0.8296129
rNum[2] 0 = 0.6523779
rNum[3] 0 = 0.6867073
Removing the Rnd(-1) entry from your second set of code gives the same results as the first set. The functions work as expected. Randomize seeds the sequence, but does not restart it - only Rnd(negative number) does that.
Basically, the first set of code is starting the random number generation at a point in the sequence which you have no control over.
You've answered your main question long ago and I'm a bit late to the party. This is my first post so I don't have the reputation to add a comment on your secondary question about rounding errors but here goes:
I recently had call to use Reflector to decompile the implementation of VBMath. I wanted to make a non static version so I could have a multiple threadsafe VB6 compatible Rnd() sequences going at the same time.
I hit the same accuracy errors as you. It took me a while but I found that Reflector (I think) bungled one of the constants:
In the Rnd(float Number) function, change:
return (((float)rndSeed) / 1.677722E+07f);
to
return (((float)rndSeed) / 16777216f);
How Visual Basic Generates Pseudo-Random Numbers for the RND Function
has the correct constant.
ANSWER
The problem is that since the legacy code doesn't call Rnd with a negative value, the generator doesn't clear its state and the call to Rnd gets chained to the previous value of the seed (in this case, the hard-coded value).
To solve the problem and be able to repeat the process all over again without all the problems that would imply "reproducing" the initial state, i cloned the generator code and patched it so i could reproduce the same situation every time depending on a parameter.
I know.. its ugly.. but it solves my problem (Btw i also know that there are some rounding errors and that the generated values are not exact.. they differ in like the last digit or something) but i don't need exact precision.
The rounding error probably comes from my choice of language for the cloning of the algorithm. If someone could help out on how to get the exact same result (match the rounding errors) that would be nice.
The patched code follows.
public sealed class RndGenerator
{
static int m_rndSeed = 0x50000;
// This is the value that the programmer sets the seed at ProjectData object
// initialization
const int CONSTANT_INIT_RNDSEED = 0x50000;
// Methods
private static float GetTimer()
{
DateTime now = DateTime.Now;
return (float)(((((60 * now.Hour) + now.Minute) * 60) + now.Second) + (((double)now.Millisecond) / 1000.0));
}
public static void Randomize()
{
float timer = GetTimer();
int rndSeed = m_rndSeed;
int num = BitConverter.ToInt32(BitConverter.GetBytes(timer), 0);
num = ((num & 0xffff) ^ (num >> 0x10)) << 8;
rndSeed = (rndSeed & -16776961) | num;
m_rndSeed = rndSeed;
}
public static void Randomize(double Number)
{
Randomize(Number, false);
}
public static void Randomize(double Number, bool useHardCodedState)
{
int num;
int rndSeed = 0;
if (useHardCodedState)
rndSeed = CONSTANT_INIT_RNDSEED;
else
rndSeed = m_rndSeed;
if (BitConverter.IsLittleEndian)
{
num = BitConverter.ToInt32(BitConverter.GetBytes(Number), 4);
}
else
{
num = BitConverter.ToInt32(BitConverter.GetBytes(Number), 0);
}
num = ((num & 0xffff) ^ (num >> 0x10)) << 8;
rndSeed = (rndSeed & -16776961) | num;
m_rndSeed = rndSeed;
}
public static float Rnd()
{
return Rnd(1f);
}
public static float Rnd(float Number)
{
int rndSeed = m_rndSeed;
if (Number != 0.0)
{
if (Number < 0.0)
{
long num3 = BitConverter.ToInt32(BitConverter.GetBytes(Number), 0);
num3 &= (long)0xffffffffL;
rndSeed = (int)((num3 + (num3 >> 0x18)) & 0xffffffL);
}
rndSeed = (int)(((rndSeed * 0x43fd43fdL) + 0xc39ec3L) & 0xffffffL);
}
m_rndSeed = rndSeed;
return (((float)rndSeed) / 1.677722E+07f);
}
}
Just incase anyone wanted here is my Java version
public class Rnd{
private int Xi;
private static int m = (int) Math.pow(2, 24);
private static int a = 0x43fd43fd;
private static int c = 0xc39ec3;
private static int m_rndSeed = 0x50000;
public static float Rnd() {
return Rnd(1f);
}
public static float Rnd(float number) {
int rndSeed = m_rndSeed;
if (number != 0.0) {
if (number < 0.0) {
long num3 = Float.floatToRawIntBits(number)& 0xffffffffL;
rndSeed = (int) ((num3 + (num3 >> 0x18)) & 0xffffffL);
}
rndSeed = (int) (((rndSeed * a) + c) & 0xffffffL);
}
m_rndSeed = rndSeed;
return (((float) rndSeed) / m);
}
}
Using private static int m = (int) Math.pow(2, 24); instead of 1.677722E+07f fixed my rounding issue.
I would stay away from the VB centric functions, and just use the Random class with a fixed seed (I assume the seed, if set, is not time-sensitive).

Categories