What is the fastest(or at least very fast) way to get first set(1) bit position from least significant bit (LSB) to the most significant bit (MSB) in a ulong (C#)?
For ulong i = 18; (10010) that would be 2(or 1 if we are counting position from 0).
MS C++ compiler has _BitScanForward64 Intrinsics for this task, but C# compiler doesn't have analogue.
With .NET Core 3.0 introducing hardware intrinsics, the fastest solution should be
ulong value = 18;
ulong result = System.Runtime.Intrinsics.X86.Bmi1.X64.TrailingZeroCount(value);
Alternatively, the new System.Numerics.Bitoperations methods also use hardware intrinsics:
int result2 = System.Numerics.BitOperations.TrailingZeroCount(value);
public static UInt64 CountTrailingZeros(UInt64 input)
{
if (input == 0) return 64;
UInt64 n = 0;
if ((input & 0xFFFFFFFF) == 0) { n = 32; input = input >> 32; }
if ((input & 0xFFFF) == 0) { n = n + 16; input = input >> 16; }
if ((input & 0xFF) == 0) { n = n + 8; input = input >> 8; }
if ((input & 0xF) == 0) { n = n + 4; input = input >> 4; }
if ((input & 3) == 0) { n = n + 2; input = input >> 2; }
if ((input & 1) == 0) { ++n; }
return n;
}
I changed the answer of Michael D. O'Connor to match Your question.
I have measured perfomance of all answers.
The winner is not present here classic De Bruijn sequence approach.
private const ulong DeBruijnSequence = 0x37E84A99DAE458F;
private static readonly int[] MultiplyDeBruijnBitPosition =
{
0, 1, 17, 2, 18, 50, 3, 57,
47, 19, 22, 51, 29, 4, 33, 58,
15, 48, 20, 27, 25, 23, 52, 41,
54, 30, 38, 5, 43, 34, 59, 8,
63, 16, 49, 56, 46, 21, 28, 32,
14, 26, 24, 40, 53, 37, 42, 7,
62, 55, 45, 31, 13, 39, 36, 6,
61, 44, 12, 35, 60, 11, 10, 9,
};
/// <summary>
/// Search the mask data from least significant bit (LSB) to the most significant bit (MSB) for a set bit (1)
/// using De Bruijn sequence approach. Warning: Will return zero for b = 0.
/// </summary>
/// <param name="b">Target number.</param>
/// <returns>Zero-based position of LSB (from right to left).</returns>
private static int BitScanForward(ulong b)
{
Debug.Assert(b > 0, "Target number should not be zero");
return MultiplyDeBruijnBitPosition[((ulong)((long)b & -(long)b) * DeBruijnSequence) >> 58];
}
The fastest way is to inject Bit Scan Forward (bsf) Bit Instruction to assembly after JIT compiler instead of BitScanForward body, but this requires much more efforts.
public static UInt64 CountLeadingZeros(UInt64 input)
{
if (input == 0) return 64;
UInt64 n = 1;
if ((input >> 32) == 0) { n = n + 32; input = input << 32; }
if ((input >> 48) == 0) { n = n + 16; input = input << 16; }
if ((input >> 56) == 0) { n = n + 8; input = input << 8; }
if ((input >> 60) == 0) { n = n + 4; input = input << 4; }
if ((input >> 62) == 0) { n = n + 2; input = input << 2; }
n = n - (input >> 63);
return n;
}
I'll bet this'll be faster. From here.
static Int32 GetLSBPosition(UInt64 v) {
UInt64 x = 1;
for (var y = 0; y < 64; y++) {
if ((x & v) == x) {
return y;
}
x = x << 1;
}
return 0;
}
While similar to Alexander's answer, this form performs consistently faster, about 46 million operations per second on my machine.
Also, I have written it to be Zero based, but personally I think it should be 1 based, eg:
Assert.Equal(0, GetLSBPosition(0));
Assert.Equal(1, GetLSBPosition(1));
Assert.Equal(1, GetLSBPosition(3));
As a bit-wise operation, the lowest set bit is:
ulong bit = x & ~(x-1);
and the original value with the lowest on-bit set to off is:
x & (x-1)
So to get all the bits that are on:
public static void Main()
{
ulong x = 13;
while(x > 0)
{
ulong bit = x & ~(x-1);
x = x & (x-1);
Console.WriteLine("bit-value {0} is set", bit);
}
}
Output
bit-value 1 is set
bit-value 4 is set
bit-value 8 is set
The solution with a very fast bit operations. Only unsafe code can be faster.
ulong n = 18; // 10010
ulong b = 1;
int p = 0;
for (int i = 0; i < 64; i++)
{
if ((n & b) == b)
{
p = i;
break;
}
b = b << 1;
}
Console.WriteLine(p);
Related
I always have to close Unity through Task-Manager if I call this function through a button, what is wrong with my code? I know there are is a lot of processing required for this code but the process never finishes:
public void SudokuLösen()
{
for(int p = 1; p < 81; p++)
{
if(PositionWert[p] != 0){
for(int z = 1; z < 9; z++)
{
if(PositionWert[p] == z)
{
int Reihe = 0;
int Spalte = 0;
int Platz = 0;
for(int m = 0; m < 8; m++)
{
for(int r = 1; r < 9; r++)
{
if(p - m * 9 == r)
{
Reihe = r;
}
}
}
for(int s = 0; s < 9; s++)
{
if(p == Reihe * 9 - 9 + s )
{
s = Spalte;
}
}
for(int g = -8; g < 8; g++)
{
if((Spalte + g > 0) && (Spalte + g < 10))
{
Blockiert[(p + z * 81 - 81) + g] = true;
}
if((Reihe + g > 0) && (Reihe + g < 10))
{
Blockiert[(p + z * 81 - 81) + g * 9] = true;
}
}
for(int vx = 1; vx < 3; vx++)
{
if((Spalte == 0 + vx) || (Spalte == 3 + vx) || (Spalte == 6 + vx))
{
Platz += vx;
}
if((Reihe == 0 + vx) || (Reihe == 3 + vx) || (Reihe == 6 + vx))
{
Platz += (3 * vx - 3);
}
}
for(int q = 1; q < 4; q++)
{
Blockiert[(p + z * 81 - 81) + Quadrat[Platz * 4 - 4 + q]] = true;
}
}
}
}
}
}
}
the Arrays are defined like this:
public static bool[] Blockiert = new bool[730];
public static int[] PositionWert = new int[82];
private int[] Quadrat = {0, 10, 11, 19, 20, 8, 10, 17, 19, 7, 8, 16, 17, -8, -7, 10, 11, -10, -8, 8, 10, -11, -10, 7, 8, -17, -16, -8, -7, -19, -17, -10, -8, -20, -19, -11, -10};
I thought that it might just be because my computer would just take forever to call this function so I waited a long time and saw that Unity nearly used 15% of my CPU so it didn't really do much and the function processing just doesn't finish.
Does anyone know what is wrong or what I should change?
A freeze usually means you have a never ending loop somewhere.
All your loops are finite and there is nothing that should take longer than a couple of milliseconds - except one!
You have
for(int s = 0; s < 9; s++)
{
if(p == Reihe * 9 - 9 + s)
{
s = Spalte;
}
}
where you reassign s = 0 since Spalte was never assigned differently. So you restart the loop and if this condition is met once then it will definitely be met in a future iteration as well since none of the other parameters change within that loop => never ending loop.
Judging from the rest of your code it should probably rather be
Spalte = s;
This piece of code was converted from my own Pascal code written in 1999 for handling the .ICO viewer....
It actually reduce the color depth from 256-color downto 16-color.
The following is in C#:
I barely understand the logic behind the code now, that I could not explain it to GitHub readers what is this essential part is about for my Image to (colorful)ASCII converter....
What I know is it takes RGB values and return (16-color)color code (ConsoleColor in C#). I need further help in understanding it.
It might have something to do with the strange Pascal way of evaluating condition and nested iteration construct....
static int SimplifyColorComponent(int Value)
{
if (Value >= 52)
return 63;
else if (Value >= 32)
return 42;
else if (Value >= 12)
return 21;
return 0;
}
static byte DecreaseColor256(byte Red, byte Green, byte Blue)
{
byte[,] Palette16 = new byte[16,3] { {0, 0, 0 }, {0, 0, 42 }, {0, 42, 0 }, {0, 42, 42 }, { 42, 0, 0 }, {42, 0, 42}, {42, 42, 0}, {42, 42, 42}, { 0, 0, 21}, {0, 0, 63}, {0, 42, 21}, {0, 42, 63}, { 42, 0, 21}, { 42, 0, 63}, { 42, 42, 21}, { 42, 42, 63} };
byte Color;
byte Component;
byte Value = 0;
byte NewRed = 0;
byte NewGreen = 0;
byte NewBlue = 0;
for (Component = 0; Component <= 2; Component++)
{
if (Component == 0)
Value = (byte)SimplifyColorComponent(Red / 4);
else if (Component == 1)
Value = (byte)SimplifyColorComponent(Green / 4);
else if (Component == 2)
Value = (byte)SimplifyColorComponent(Blue / 4);
Color = 0;
while (Value != Palette16[Color, Component])
{
Color++;
if (Color > 15)
{
Value -= 21;
Color = 0;
}
}
if (Component == 0)
NewRed = Value;
else if (Component == 1)
NewGreen = Value;
else if (Component == 2)
NewBlue = Value;
}
for (Color = 0; Color <= 15; Color++)
if ((Palette16[Color, 0] == NewRed) && (Palette16[Color, 1] == NewGreen) && (Palette16[Color, 2] == NewBlue))
return Color;
return 0;
}
Converted line by line from my legacy Pascal code:
function SimplifyColorComponent(Value:byte):byte;
begin
if Value>=52 then
SimplifyColorComponent:=63
else
if Value>=32 then
SimplifyColorComponent:=42
else
if Value>=12 then
SimplifyColorComponent:=21
else
SimplifyColorComponent:=0;
end;
function DecreaseColor256(Red,Green,Blue:byte):byte;
const
Palette16:array [0..15,1..3] of byte=((0,0,0),(0,0,42),(0,42,0),(0,42,42),
(42,0,0),(42,0,42),(42,42,0),(42,42,42),
(0,0,21),(0,0,63),(0,42,21),(0,42,63),
(42,0,21),(42,0,63),(42,42,21),(42,42,63));
var
Color,Component,Value:byte;
NewRed,NewGreen,NewBlue:byte;
begin
DecreaseColor256:=0;
Component:=1;
repeat
case Component of
1:Value:=SimplifyColorComponent(Red div 4);
2:Value:=SimplifyColorComponent(Green div 4);
3:Value:=SimplifyColorComponent(Blue div 4);
end;
Color:=0;
while Value<>Palette16[Color,Component] do
begin
Inc(Color);
if Color>15 then
begin
Dec(Value,21);
Color:=0;
end;
end;
case Component of
1:NewRed:=Value;
2:NewGreen:=Value;
3:NewBlue:=Value;
end;
Inc(Component);
until Component>3;
for Color:=0 to 15 do
if (Palette16[Color,1]=NewRed) and (Palette16[Color,2]=NewGreen)
and (Palette16[Color,3]=NewBlue) then
begin
DecreaseColor256:=Color;
Exit;
end;
end;
I'm trying to calculate the number of possible combinations so I'm using some maths here (to be precise factorials). For example, if I have 50 numbers and I want to organize them into groups of 5, how many groups (combinations) are possible to make. I'm using this formula: allNumbers! / (allNumbers - PerGroup)!, but it comes up with an error for this particular example. It says that dividing by zero is forbidden. How can I manage this to work?
This is my code:
int b = 1;
int n = 1;
if (allNumbers - PerGroup == 0)
{
return 1;
}
else if (allNumbers - PerGroup == 1)
{
return allNumbers;
}
else
{
for (int i = 1; i <= allNumbers; i++)
{
b *= i;
}
for (int i = 1; i <= allNumbers - PerGroup; i++)
{
n *= i;
}
if (Enumerable.Range(1,int.MaxValue).Contains(b/n)) //line with ERROR!
{
return b/n;
}
else
{
return int.MaxValue;
}
}
Enumerable.Range(1,int.MaxValue).Contains(b/n) check doesn't check if the value is valid, because b/n is already computed and stored as int by this time.
You get division by zero because variable n is overflowed and becomes zero. In the following code you can see how overflow occurs.
using System;
public class Test
{
public static void Main()
{
int n = 1;
for (int i = 1; i <= 50; i++) {
n *= i;
Console.WriteLine("i = {0}, n = {1}", i, n);
}
}
}
Output:
i = 1, n = 1
i = 2, n = 2
i = 3, n = 6
i = 4, n = 24
i = 5, n = 120
i = 6, n = 720
i = 7, n = 5040
i = 8, n = 40320
i = 9, n = 362880
i = 10, n = 3628800
i = 11, n = 39916800
i = 12, n = 479001600
i = 13, n = 1932053504
i = 14, n = 1278945280
i = 15, n = 2004310016
i = 16, n = 2004189184
i = 17, n = -288522240
i = 18, n = -898433024
i = 19, n = 109641728
i = 20, n = -2102132736
i = 21, n = -1195114496
i = 22, n = -522715136
i = 23, n = 862453760
i = 24, n = -775946240
i = 25, n = 2076180480
i = 26, n = -1853882368
i = 27, n = 1484783616
i = 28, n = -1375731712
i = 29, n = -1241513984
i = 30, n = 1409286144
i = 31, n = 738197504
i = 32, n = -2147483648
i = 33, n = -2147483648
i = 34, n = 0
i = 35, n = 0
i = 36, n = 0
i = 37, n = 0
i = 38, n = 0
i = 39, n = 0
i = 40, n = 0
i = 41, n = 0
i = 42, n = 0
i = 43, n = 0
i = 44, n = 0
i = 45, n = 0
i = 46, n = 0
i = 47, n = 0
i = 48, n = 0
i = 49, n = 0
i = 50, n = 0
since allNumbers! always contains (allNumbers - PerGroup)!, why don't you exclude them from start.
int b = 1;
if (allNumbers - PerGroup == 0)
{
return 1;
}
else if (allNumbers - PerGroup == 1)
{
return allNumbers;
}
else
{
for (int i = (allNumbers - PerGroup + 1); i <= allNumbers; i++)
{
b *= i;
}
return b;
}
I guess the error is OutOfMemoryException because you are creating a huge amount of unnecessary integers. (Enumerable.Range(1,int.MaxValue)) note that each int takes 4 bytes from your memory.
Im not sure what you are trying to do there but you can use double type so if the number becomes very large it will just give you PositiveInfinity.
Or you can use checked to control integer overflow with try and catch.
Another way is to pre calculate the number when integer overflow happens. for example factorial of 14 will overflow for int and factorial of 22 will overflow for long.
Also you dont have to write for loop twice. you can use method for this purpose.
You dont need to check for allNumbers - PerGroup == 0 to prevent zero division. that wont happen because factorial of 0 is 1 and our factorial implementation returns 1 by its nature when the input is 0! (because the for loop never iterates in that case and the counter starts from 1.)
private static int Cominations(int allNumbers, int perGroup)
{
if(allNumbers > 13)
{
Console.WriteLine("Too big number!");
return -1;
}
return Factorial(allNumbers)/Factorial(allNumbers - perGroup);
}
private static int Factorial(int number)
{
int n = 1;
for (int i = 1; i < number; i++)
{
n *= i;
}
return n; // returns 1 when number is 0
}
If you want to calculate factorial of bigger numbers use BigInteger type from System.Numberics namespace.
using System.Numerics;
//...
private static BigInteger Factorial(int number)
{
BigInteger n = 1;
for (int i = 1; i < number; i++)
{
n *= i;
}
return n;
}
I am trying to determine what the neighbour bets would be for a given number on a roulette wheel.
At the moment I pass a pocket number to a function and below is my code. "pocket_number()[0]" is the value of the number I want to determine the neighbours of.
int[] neightbourbets = neighbourbets(pocket_number()[0]);
neighbourbets() is as follows (this outputs an array of element numbers so elsewhere in my program I can extract them from the same array structure). At the moment I have a crude way of determining the the neighbour bets and getting the function to state which numbers are 6 numbers either side of it.
Is there a better way to do this? I've found one of the problems (that I've overcome with the below) is if I want to know the neighbours for "0" for example which means the code needs to get the numbers from the end of the array.
public int[] neighbourbets(int x)
{
int[] pocket_array = new[] {0, 32, 15, 19, 4, 21, 2, 25, 17, 34, 6, 27, 13, 36, 11, 30, 8, 23, 10, 5, 24, 16, 33, 1, 20, 14, 31, 9, 22, 18, 29, 7, 28, 12, 35, 3, 26};
int predictednum = Array.IndexOf(pocket_array,x); //element number of pocket_array for chosen number
int[] neighbourbets = new[] {0,0,0,0,0,0,0,0,0,0,0,0,0};
neighbourbets[0] = predictednum;
neighbourbets[1] = predictednum+1;
neighbourbets[2] = predictednum+2;
neighbourbets[3] = predictednum+3;
neighbourbets[4] = predictednum+4;
neighbourbets[5] = predictednum+5;
neighbourbets[6] = predictednum+6;
neighbourbets[7] = predictednum-1;
neighbourbets[8] = predictednum-2;
neighbourbets[9] = predictednum-3;
neighbourbets[10] = predictednum-4;
neighbourbets[11] = predictednum-5;
neighbourbets[12] = predictednum-6;
for (int i = 0; i < neighbourbets.Length; i++)
{
//clockwise neighours
if (neighbourbets[i] == -1) {
neighbourbets[i] = 36;
}
if (neighbourbets[i] == -2) {
neighbourbets[i] = 35;
}
if (neighbourbets[i] == -3) {
neighbourbets[i] = 34;
}
if (neighbourbets[i] == -4) {
neighbourbets[i] = 33;
}
if (neighbourbets[i] == -5) {
neighbourbets[i] = 32;
}
if (neighbourbets[i] == -6) {
neighbourbets[i] = 31;
}
//anticlockwise neighbours
if (neighbourbets[i] == 37) {
neighbourbets[i] = 0;
}
if (neighbourbets[i] == 38) {
neighbourbets[i] = 1;
}
if (neighbourbets[i] == 39) {
neighbourbets[i] = 2;
}
if (neighbourbets[i] == 40) {
neighbourbets[i] = 3;
}
if (neighbourbets[i] == 41) {
neighbourbets[i] = 4;
}
if (neighbourbets[i] == 42) {
neighbourbets[i] = 5;
}
}
return neighbourbets;
}
Any helps or guidence is appreciated! :)
Write a small helper function to wrap around the index:
private int GetPocketIndex( int start, int offset, int count )
{
int pos = ( start + offset ) % count;
if( pos >= 0 )
return pos;
else
return count + pos; // pos is negative so we use +
}
The modulus there will help it wrap around when it goes above the maximum, and the if will do it for the minimum. This could probably be done easier though, but it eludes me at the moment.
Then, if you need that specific order, perhaps something like this:
int[] offsets = new int[] { 0,
1, 2, 3, 4, 5, 6,
-1, -2, -3, -4, -5, -6 };
int[] neighbourbets = new int[offsets.Length];
for( int i = 0; i < offsets.Length; i++ )
neighbourbets[i] = GetPocketIndex( predictednum, offsets[i], pocket_array.Length );
Or, if any order will do:
int count = 6;
int[] neighbourbets = new int[count * 2 + 1];
for( int i = 0; i < neighbourbets.Length; i++ )
neightbourbets[i] = GetPocketIndex( predictednum, i - count, pocket_array.Length );
The following would give you the result with the x in the middle of the result array and the neighbours to the left and right of it:
public static int[] neighbourbets2(int x, int neighborCount)
{
int[] pocket_array = new[] { 0, 32, 15, 19, 4, 21, 2, 25, 17, 34, 6, 27, 13, 36, 11, 30, 8, 23, 10, 5, 24, 16, 33, 1, 20, 14, 31, 9, 22, 18, 29, 7, 28, 12, 35, 3, 26 };
int predictednum = Array.IndexOf(pocket_array, x);
// Initialize the result array. Its size is double the neighbour count + 1 for x
int[] result = new int[neighborCount * 2 + 1];
// Calc the start index. We begin at the most left item.
int startAt = predictednum - neighborCount;
// i - position in the result array
// j - position in the pocket_array
for (int i = 0, j = startAt; i < result.Length; i++, j++)
{
// Adjust j if it's less then 0 to wrap around the array.
result[i] = pocket_array[j < 0 ? j + pocket_array.Length : j];
// If we are at the end then start from the beginning.
if (j == pocket_array.Length)
{
j = 0;
}
}
return result;
}
I have been reading the SWF format available on Adobe's site and it mentions that in order to save space, variable bits are used to store integers or floats (page 17 in the pdf)
I have always worked with byte-aligned data so have not given much thought to files that are bit-aligned, or have variable alignment where the information is stored in each byte.
So for example, you may have a struct containing four 13-bit integers stored sequentially ( rather than storing them as four 16-bit integers).
The first 13bits is the first integer, the next 13 bits is the second integer, and so on. It pads the last byte appropriate to make the struct byte-aligned with the rest of the file, so 52-bits would be padded to 56-bits, requiring 7 bytes to store those four integers as opposed to 8 bytes.
How do I approach this kind of problem?
How can I work with a stream of bytes at the bit-level?
Is there something I can use to help make it easier to work with this data?
I imagine the solution boils down to using bit-operations on byte arrays.
An example solution for parsing the four 13-bit integers would be nice as well to demonstrate the use of your suggested method.
There are two ways of dealing with this that I know of. The first is to manually do it - using bit-wise operators, division, modulus etc. on byte arrays [or integer/ulong etc if you were bored]. IsBitSet Example
The other way is a BitArray - which handles most of this for you :)
It would be nice to add an example of how exactly BitArray handles getting bits 13..25 as an int, as that would be the primary operation. At a first glance I see only a loop.
Fine... I wrote a quick & dirty test proof of concept:
var rnd = new Random();
//var data = Enumerable.Range(0, 10).ToArray();
var data = Enumerable.Range(0, 10).Select(x => rnd.Next(1 << 13)).ToArray();
foreach (var n in data) Console.WriteLine(n);
Console.WriteLine(new string('-', 13));
var bits = new BitArray(data.Length * 13);
for (int i = 0; i < data.Length; i++)
{
var intBits = new BitArray(new[] { data[i] });
for (int b = 12; b > -1; b--)
{
bits[i * 13 + b] = intBits[b];
Console.Write(intBits[b] ? 1 : 0);
}
Console.WriteLine();
}
Console.WriteLine(new string('-', 13));
for (int i = 0; i < bits.Length / 13; i++)
{
int number = 0;
for (int b = 12; b > -1; b--)
if (bits[i * 13 + b])
number += 1 << b;
Console.WriteLine(number);
}
Console.ReadLine();
Which outputs:
910
3934
7326
7990
7712
1178
6380
3460
5113
7489
-------------
0001110001110
0111101011110
1110010011110
1111100110110
1111000100000
0010010011010
1100011101100
0110110000100
1001111111001
1110101000001
-------------
910
3934
7326
7990
7712
1178
6380
3460
5113
7489
The bit array doesn't do much other than simplify accessing - it's still quite manual. I expect you'd write your own classes to simply this and make it neat and reusable - for example here's another quick concept:
//Improved to take sign into account.
//Sign is in addition to bits allocated for storage in this version.
//Stored as {sign}{bits}
//E.g. -5, stored in 3 bits signed is:
// 1 101
//E.g. 5, stored in 3 bits [with sign turned on]
// 0 101
//E.g. 5, stored in 3 bits no sign
// 101
//This may differ from your exiting format - e.g. you may use two's compliments.
static void Main(string[] args)
{
int bitsPerInt = 13;
//Create your data
var rnd = new Random();
//var data = Enumerable.Range(-5, 10).ToArray();
var data = Enumerable.Range(0, 10).Select(x => rnd.Next(-(1 << bitsPerInt), 1 << bitsPerInt)).ToArray();
var bits = new BitSerlializer();
//Add length header
bits.AddInt(data.Length, 8, false);
foreach (var n in data)
{
bits.AddInt(n, bitsPerInt);
Console.WriteLine(n);
}
//Serialize to bytes for network transfer etc.
var bytes = bits.ToBytes();
Console.WriteLine(new string('-', 10));
foreach (var b in bytes) Console.WriteLine(Convert.ToString(b, 2).PadLeft(8, '0'));
Console.WriteLine(new string('-', 10));
//Deserialize
bits = new BitSerlializer(bytes);
//Get Length Header
var count = bits.ReadInt(8, false);
for (int i = 0; i < count; i++)
Console.WriteLine(bits.ReadInt(bitsPerInt));
Console.ReadLine();
}
public class BitSerlializer
{
List<byte> bytes;
int Position { get; set; }
public BitSerlializer(byte[] initialData = null)
{
if (initialData == null)
bytes = new List<byte>();
else
bytes = new List<byte>(initialData);
}
public byte[] ToBytes() { return bytes.ToArray(); }
public void Addbit(bool val)
{
if (Position % 8 == 0) bytes.Add(0);
if (val) bytes[Position / 8] += (byte)(128 >> (Position % 8));
Position++;
}
public void AddInt(int i, int length, bool isSigned = true)
{
if (isSigned) Addbit(i < 0);
if (i < 0) i = -i;
for (int pos = --length; pos >= 0; pos--)
{
var val = (i & (1 << pos)) != 0;
Addbit(val);
}
}
public bool ReadBit()
{
var val = (bytes[Position / 8] & (128 >> (Position % 8))) != 0;
++Position;
return val;
}
public int ReadInt(int length, bool isSigned = true)
{
var val = 0;
var sign = isSigned && ReadBit() ? -1 : 1;
for (int pos = --length; pos >= 0; pos--)
if (ReadBit())
val += 1 << pos;
return val * sign;
}
}
On the other hand, byte-array-based approach could go like this:
int extend(uint raw, int bits)
{
int sh = 32 - bits;
int x = (int)raw << sh; // puts your sign bit in the highest bit.
return x >> sh; // since x is signed this is an arithmatic signed shift
}
int read(byte[] data, int pos, int bits, bool signed)
{
int fbi = pos / 8; // first byte index
int lbi = (pos + bits - 1) / 8; // last byte index
int cnt = lbi - fbi + 1; // bytes spanned
if (cnt > 3 || lbi >= data.Length) { throw new ArgumentException(); }
uint raw = (uint)(
(data[fbi] << (24 + pos % 8)) +
(cnt < 2 ? 0 : data[fbi + 1] << (16 + pos % 8)) +
(cnt < 3 ? 0 : data[fbi + 2] << (8 + pos % 8))
) >> (32 - bits);
return signed ? extend(raw, bits) : (int)raw;
}
Test for this:
byte[] test = { 0x55, 0xAA, 0x10 };
string s = "";
s += read(test, 0, 8, false) + "\r\n";
s += read(test, 0, 8, true) + "\r\n";
s += read(test, 8, 8, false) + "\r\n";
s += read(test, 8, 8, true) + "\r\n";
s += read(test, 4, 8, false) + "\r\n";
s += read(test, 7, 9, true) + "\r\n";
s += read(test, 7, 10, true) + "\r\n";
s += read(test, 7, 11, true) + "\r\n";
s += read(test, 7, 12, true) + "\r\n";
s += read(test, 7, 13, true) + "\r\n";
s += read(test, 7, 14, true) + "\r\n";
s += read(test, 7, 15, true) + "\r\n";
s += read(test, 7, 16, true) + "\r\n";
s += read(test, 7, 17, true) + "\r\n";
s += read(test, 18, 2, true) + "\r\n";
s += read(test, 18, 3, true) + "\r\n";
s += read(test, 23, 1, true) + "\r\n";
s += read(test, 23, 2, true) + "\r\n";
The test builds the string like the following:
85
85
170
-86
90
-86
-172
-344
-688
-1375
-2750
-5500
-11000
-22000
1
2
0
then throws an exception on the last line.