簡體   English   中英

在ushort中設置比特范圍

[英]Set Range of Bits in a ushort

假設我有一個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。

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(或者如果你需要那么長)可能更慣用。

這里的固定類型答案幫助我按原始要求獲得通用解決方案。 這是最終的代碼(帶有吸氣劑獎勵)。

/// <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));
}

如果您想要這種類型的訪問,請考慮使用BitVector32BitArray ,或熟悉按位算術。 可以在C#中明確布局結構(導致聯合),但我不認為這對您有利。 它主要針對互操作場景。

對於info,按位運算符主要在uint / int / ulong / long - 而不是更小的整數類型。

我不知道任何語言功能(除了手動移位),這將實現這一目標。

我唯一想到的是來自.NET 1.1的舊BitArray 但是你只能操縱各個位,而不是位的范圍。

所以不行。

以下是您的更新修復:

  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);
  }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM