假设我有一个ushort值,我想设置位1到4(假设0是LSB,15是MSB)。
在C ++中,您可以定义一个映射出特定位的结构:
struct KibblesNBits
{
unsigned short int TheStart: 1;
unsigned short int TheMeat: 4;
unsigned short int TheRest: 11;
}
然后你可以直接为'TheMeat'赋值。我想在C#中做类似的事情。理想情况下,我想要一个看起来像这样的功能定义:
public ModValue SetRange<ModValue, RangeValue>(ModValue valueToMod, int startIndex, int endIndex, RangeValue rangeValueToAssign)
还需要确定rangeValueToAssign不超过最大大小(假设值从0到最大值无符号)。因此,如果范围是1到4,则为4位,范围将从0到15.如果超出这些限制,则抛出异常。
我在BitConverter类中找不到任何可以执行此类操作的内容。我能想到的最好的就是使用manaul移位运算符。有更好的方法吗?
编辑:非通用版本可能如下所示:
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);
}
其中:
ushort new_val = SetRange(120, 1, 2, 3);
会导致'new_val'设置为126。
答案 0 :(得分:2)
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);
}
没有for循环或Math.Pow(速度非常慢,比Sin / Cos等慢)。
至于通用 - 抱歉,这不起作用。 C#(或.NET)中的数字没有基类型,所以这根本不可能。看起来你正试图在C ++中使用像模板函数这样的泛型 - 不要被类似的外观所迷惑;他们完全不同。
如果你必须有不同的类型,我建议改为重载。
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));
}
然而,在C#中,总是使用int(或者如果你需要那么长)可能更惯用。
答案 1 :(得分:1)
这里的固定类型答案帮助我获得了最初要求的通用解决方案。这是最终的代码(附赠)。
/// <summary>Gets the bit array value from the specified range in a bit vector.</summary>
/// <typeparam name="T">The type of the bit vector. Must be of type <see cref="IConvertible"/>.</typeparam>
/// <param name="bits">The bit vector.</param>
/// <param name="startIdx">The zero-based start index of the bit range to get.</param>
/// <param name="count">The number of sequential bits to fetch starting at <paramref name="startIdx"/>.</param>
/// <returns>The value of the requested bit range.</returns>
public static T GetBits<T>(T bits, byte startIdx, byte count) where T : IConvertible
{
if (startIdx >= (Marshal.SizeOf(typeof(T)) * 8)) throw new ArgumentOutOfRangeException(nameof(startIdx));
if (count + startIdx > (Marshal.SizeOf(typeof(T)) * 8)) throw new ArgumentOutOfRangeException(nameof(count));
return (T)Convert.ChangeType((bits.ToInt64(null) >> startIdx) & ((1 << count) - 1), typeof(T));
}
/// <summary>Sets the bit values at the specified range in a bit vector.</summary>
/// <typeparam name="T">The type of the bit vector. Must be of type <see cref="IConvertible"/>.</typeparam>
/// <typeparam name="TValue">The type of the value. Must be of type <see cref="IConvertible"/>.</typeparam>
/// <param name="bits">The bit vector.</param>
/// <param name="startIdx">The zero-based start index of the bit range to set.</param>
/// <param name="count">The number of sequential bits to set starting at <paramref name="startIdx"/>.</param>
/// <param name="value">The value to set within the specified range of <paramref name="bits"/>.</param>
public static void SetBits<T, TValue>(ref T bits, byte startIdx, byte count, TValue value) where T : IConvertible where TValue : IConvertible
{
if (startIdx >= (Marshal.SizeOf(typeof(T)) * 8)) throw new ArgumentOutOfRangeException(nameof(startIdx));
if (count + startIdx > (Marshal.SizeOf(typeof(T)) * 8)) throw new ArgumentOutOfRangeException(nameof(count));
var val = value.ToInt64(null);
if (val >= (1 << count)) throw new ArgumentOutOfRangeException(nameof(value));
bits = (T)Convert.ChangeType(bits.ToInt64(null) & ~(((1 << count) - 1) << startIdx) | (val << startIdx), typeof(T));
}
答案 2 :(得分:0)
如果您想要这种类型的访问权限,请考虑BitVector32
或BitArray
,或熟悉按位算术。你可以在C#中做结构的显式布局(导致联合),但我不认为这对你有利。它主要针对互操作场景。
有关信息,按位运算符主要用于uint
/ int
/ ulong
/ long
- 而不是较小的整数类型。
答案 3 :(得分:0)
答案 4 :(得分:-1)
以下是您的更新修补程序:
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);
}