LoginSignup
1
1

More than 5 years have passed since last update.

整数ビット演算

Posted at

BitArrayクラスを使わずに、数値の指定ビット位置(0から始まる)をオン・オフする

BitOperation.cs
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

public class BitOperation
{
    /// <summary>
    ///  指定したビット位置を変換する
    /// </summary>
    /// <param name="source">反転する値。</param>
    /// <param name="index">反転するビット位置。(0~)</param>
    /// <param name="bitSize">変換する値のビット数。</param>
    /// <param name="bitOn">ビットオンの場合、True。ビットオフの場合、False。</param>
    /// <returns>ビット操作を行った値。</returns>
    /// <remarks>
    /// UInt64 のサイズを超える値は変換できない
    /// </remarks>
    private static UInt64 Flip(UInt64 source, byte index, byte bitSize, bool bitOn)
    {
        byte size = (byte)(bitSize - 1);
        if (index > size)
        {
            throw new ArgumentOutOfRangeException("index", "ビット位置は、0~" + size + "の範囲で指定してください。");
        }

        UInt64 basedata = (UInt64)1 << index;
        UInt64 rc = source;

        if (bitOn == true)
        {
            // BIT ON
            rc = (UInt64)(rc | basedata);
        }
        else
        {
            // BIT OFF
            UInt64 reverse = (UInt64)(~basedata);
            rc = (UInt64)(rc & reverse);
        }

        return rc;
    }

    public static byte Flip(byte source, byte index, bool bitOn)
    {
        return (byte)Flip((UInt64)source, index, 8, bitOn);
    }

    public static Int32 Flip(Int32 source, byte index, bool bitOn)
    {
        return (Int32)Flip((UInt64)source, index, 32, bitOn);
    }

    public static UInt32 Flip(UInt32 source, byte index, bool bitOn)
    {
        return (UInt32)Flip((UInt64)source, index, 32, bitOn);
    }

    public static Int64 Flip(Int64 source, byte index, bool bitOn)
    {
        return (Int64)Flip((UInt64)source, index, 64, bitOn);
    }

    public static UInt64 Flip(UInt64 source, byte index, bool bitOn)
    {
        return Flip(source, index, 64, bitOn);
    }
}
Main.cs
using System.IO;
using System;

class Program
{
    static void Main()
    {
        byte b = 0;
        b = BitOperation.Flip(b, 0, true);
        Console.WriteLine(string.Format("0x{0:X}", b)); // 0x1
        b = BitOperation.Flip(b, 7, true);
        Console.WriteLine(string.Format("0x{0:X}", b)); // 0x81
        b = BitOperation.Flip(b, 7, false);
        Console.WriteLine(string.Format("0x{0:X}", b)); // 0x1
        b = BitOperation.Flip(b, 0, false);
        Console.WriteLine(string.Format("0x{0:X}", b)); // 0x0
        UInt64 ui = 0;
        ui = BitOperation.Flip(ui, 0, true);
        Console.WriteLine(string.Format("0x{0:X}", ui)); // 0x1
        ui = BitOperation.Flip(ui, 63, true);
        Console.WriteLine(string.Format("0x{0:X}", ui)); // 0x8000000000000001
        ui = BitOperation.Flip(ui, 63, false);
        Console.WriteLine(string.Format("0x{0:X}", ui)); // 0x1
        ui = BitOperation.Flip(ui, 0, false);
        Console.WriteLine(string.Format("0x{0:X}", ui)); // 0x0
    }
}

ジェネリックを使うと

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

public class BitOperation
{
    private static UInt64 Flip(UInt64 source, byte index, byte bitSize, bool bitOn)
    {
        byte size = (byte)(bitSize - 1);
        if (index > size)
        {
            throw new ArgumentOutOfRangeException("index", "ビット位置は、0~" + size + "の範囲で指定してください。");
        }

        UInt64 basedata = (UInt64)1 << index;
        UInt64 rc = source;

        if (bitOn == true)
        {
            // BIT ON
            rc = (UInt64)(rc | basedata);
        }
        else
        {
            // BIT OFF
            UInt64 reverse = (UInt64)(~basedata);
            rc = (UInt64)(rc & reverse);
        }

        return rc;
    }

    public static T Flip<T>(T source, byte index, bool bitOn)
     where T : struct, IConvertible
    {
        Type t = typeof(T);
        UInt64 src = (UInt64)Convert.ChangeType(source, typeof(UInt64));
        UInt64 to = Flip(src, index, (byte)(Marshal.SizeOf(t) * 8), bitOn);
        return (T)Convert.ChangeType(to, t);
    }
}

struct, IConvertible指定はsourceを数値型とみなす(気休め程度)
Convert.ChangeTypeの第1引数はIConvertibleを実装するオブジェクトだからということも)

Convert.ChangeTypeメソッド

ジェネリックの方は、intで指定するとOverflowExceptionが発生する場合がある。
Convert.ChangeTypeがuint→intなどの縮小変換はcheckedコンテキストで動作するためらしい。

.NET Framework における型変換 (Convertクラス 基本型どうしの変換)

縮小変換は checked コンテキストで実行され、変換が失敗すると OverflowException がスローされます。

//OverflowExceptionが発生。
//int i = BitOperation.Flip<int>(0, 31, true);
//uintで呼び出してintにキャストすれば回避できる
int ii = (int)BitOperation.Flip<uint>(0, 31, true);
Console.WriteLine(string.Format("0x{0:X}", ii)); // 0x80000000

uint ui = BitOperation.Flip<uint>(0, 31, true);
Console.WriteLine(string.Format("0x{0:X}", ui)); // 0x80000000
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1