tags:

views:

278

answers:

4

Lets say I have a ushort value that I would like to set bits 1 to 4 inclusive (assuming 0 is the LSB and 15 is the MSB).

In C++ you could define a struct that mapped out specific bits:

struct KibblesNBits
{
   unsigned short int TheStart: 1;
   unsigned short int TheMeat:  4;
   unsigned short int TheRest: 11;
}

Then you could assign a value to 'TheMeat' directly. I'm looking to do something similar in C#. Ideally, I would like a funcion definition that looked like this:

public ModValue SetRange<ModValue, RangeValue>(ModValue valueToMod, int startIndex, int endIndex, RangeValue rangeValueToAssign)

It would also need to valide that the rangeValueToAssign does not exceed the maximum size (assuming values are unsigned from 0 to max). So if the range is from 1 to 4, this is 4 bits, range would be from 0 to 15. If it is outside these limits, throw an exception.

I didnt find anything in the BitConverter class that could do something like this. Best I could think of was using manaul shift operators. Is there a better way to do this?

Edit: A non generic version might look something like this:

  public static ushort SetRange(ushort valueToMod, int startIndex, int endIndex, ushort rangeValueToAssign)
  {
     // Determine max value
     ushort max_value = Convert.ToUInt16(Math.Pow(2.0, (endIndex - startIndex) + 1.0) - 1);
     if(rangeValueToAssign > max_value) throw new Exception("Value To Large For Range");
     // Shift the value and add it to the orignal (effect of setting range?)
     ushort value_to_add = (ushort)(rangeValueToAssign << startIndex);
     return (ushort)(valueToMod + value_to_add);
  }

Where:

ushort new_val = SetRange(120, 1, 2, 3);

would result in 'new_val' being set to 126.

A: 

If you want that type of access, consider a BitVector32 or BitArray, or get familiar with bitwise arithmetic. You can do explicit layout of structs in C# (causing a union), but I don't think it is to your advantage. It is primarily aimed at interop scenarios.

For info, the bitwise operators work mainly on uint/int/ulong/long - not so much the smaller integer types.

Marc Gravell
@Marc - Thanks, do you know off hand if either of those have a SetRange feature that I am looking for. I dont see anything in the msdn pages for either.
SwDevMan81
@SwDevMan81 - no but you could write an extension method for them easily enough?
Marc Gravell
A: 

I don't know of any language feature (apart from manually shifting bits around) that would achieve this.

The only thing that comes to my mind is the old BitArray from .NET 1.1. But you can only manipulate individual bits, not ranges of bits.

So, no.

SealedSun
A: 

Here is a fix for your update:

  public static ushort SetRange(ushort valueToMod, int startIndex, int endIndex, ushort rangeValueToAssign)
  {
     // Determine max value
     ushort max_value = Convert.ToUInt16(Math.Pow(2.0, (endIndex - startIndex) + 1.0) - 1);
     if(rangeValueToAssign > max_value) throw new Exception("Value To Large For Range");
     // Clear our bits where we want to "Set" the value for
     for( int i=startIndex; i<endIndex; i++ )
         valueToMod &= ~(1<<i);
     // Shift the value and add it to the orignal (effect of setting range?)
     ushort value_to_add = (ushort)(rangeValueToAssign << startIndex);
     return (ushort)(valueToMod + value_to_add);
  }
TJMonk15
Nice, ok now just make it generic and I'm sold
SwDevMan81
Ghhh! Math.Pow? Ugly! Use bitwise shift (like in your for loop) instead.
vladr
Umm, that was his code....
TJMonk15
+1  A: 
public static int SetRange(int num, int from, int to, int value)
{
    if (from < 0 || from > to || to >= 32) throw new ArgumentException("from/to are not valid");
    if (value >= (2 << (to - from)) && (to - from < 31)) throw new ArgumentException("value is too large");
    return num & ~(((2 << to) - 1) - ((1 << from) - 1)) | (value << from);
}

No for-loops or Math.Pow (which is amazingly slow, way slower than Sin/Cos etc).

As for generic - sorry, that won't work. There is no base type for numbers in C# (or .NET), so this is simply impossible. It looks like you're trying to use generics like template functions in C++ - don't be fooled by the similar looks; they are completely different.

If you must have different types, I'd suggest overloads instead.

public static int SetRange(int num, int from, int to, int value)
{
    if (from < 0 || from > to || to >= 32) throw new ArgumentException("from/to are not valid");
    if (value >= (2 << (to - from)) && (to - from < 31)) throw new ArgumentException("value is too large");
    return num & ~(((2 << to) - 1) - ((1 << from) - 1)) | (value << from);
}

public static ushort SetRange(ushort num, int from, int to, ushort value)
{
    if (from < 0 || from > to || to >= 16) throw new ArgumentException("from/to are not valid");
    if (value >= (2 << (to - from))) throw new ArgumentException("value is too large");
    return (ushort) (num & ~(((2 << to) - 1) - ((1 << from) - 1)) | (value << from));
}

However, in C# it might be more idiomatic to just always use int (or long if you need that).

romkyns