BigInteger - Hex/Decimal/Octal/Binary string?

В Java я мог бы сделать

BigInteger b = new BigInteger(500);

Затем отформатируйте его, как мне понравилось

b.toString(2); //binary
b.toString(8); //octal
b.toString(10); //decimal
b.toString(16); //hexadecimal

В С# я могу сделать

int num = int.Parse(b.ToString());
Convert.ToString(num,2) //binary
Convert.ToString(num,8) //octal

и т.д.. Но я могу сделать это только с значениями long и меньше. Есть ли способ распечатать BigInteger с указанной базой? Я опубликовал это, BigInteger Parse Octal String? вчера и получил решение о том, как преобразовать в основном все строки в значения BigInteger, но не получило успеха.

Ответ 1

Преобразование BigInteger в десятичную, шестнадцатеричную, двоичную, восьмеричную строку:

Начнем с значения BigInteger:

BigInteger bigint = BigInteger.Parse("123456789012345678901234567890");

База 10 и база 16

Встроенные базовые 10 (десятичные) и базовые 16 (шестнадцатеричные) обложки легки:

// Convert to base 10 (decimal):
string base10 = bigint.ToString();

// Convert to base 16 (hexadecimal):
string base16 = bigint.ToString("X");

Ведущие нули (положительные и отрицательные значения BigInteger)

Обратите внимание, что ToString("X") гарантирует, что шестнадцатеричные строки имеют начальный нуль, когда значение BigInteger является положительным. Это отличается от обычного поведения ToString("X") при преобразовании из других типов значений, где ведущие нули подавляются.

Пример:

var positiveBigInt = new BigInteger(128);
var negativeBigInt = new BigInteger(-128);
Console.WriteLine(positiveBigInt.ToString("X"));
Console.WriteLine(negativeBigInt.ToString("X"));

РЕЗУЛЬТАТ:

080
80

Цель этого поведения в качестве начального нуля означает, что BigInteger - это положительное значение - по существу, старший ноль предоставляет знак. Это необходимо (в отличие от других преобразований типа значений), поскольку BigInteger не имеет фиксированного размера; поэтому нет знакового знака. Начальный нуль определяет положительное значение, а не отрицательное. Это позволяет выполнить "круглое отключение" BigInteger значения через ToString() и обратно через Parse(). Это поведение обсуждается на странице BigInteger Structure на MSDN.

Методы расширения: BigInteger для двоичных, шестнадцатеричных и октальных

Вот класс, содержащий методы расширения для преобразования экземпляров BigInteger в двоичные, шестнадцатеричные и восьмеричные строки:

using System;
using System.Numerics;
using System.Text;

/// <summary>
/// Extension methods to convert <see cref="System.Numerics.BigInteger"/>
/// instances to hexadecimal, octal, and binary strings.
/// </summary>
public static class BigIntegerExtensions
{
  /// <summary>
  /// Converts a <see cref="BigInteger"/> to a binary string.
  /// </summary>
  /// <param name="bigint">A <see cref="BigInteger"/>.</param>
  /// <returns>
  /// A <see cref="System.String"/> containing a binary
  /// representation of the supplied <see cref="BigInteger"/>.
  /// </returns>
  public static string ToBinaryString(this BigInteger bigint)
  {
    var bytes = bigint.ToByteArray();
    var idx = bytes.Length - 1;

    // Create a StringBuilder having appropriate capacity.
    var base2 = new StringBuilder(bytes.Length * 8);

    // Convert first byte to binary.
    var binary = Convert.ToString(bytes[idx], 2);

    // Ensure leading zero exists if value is positive.
    if (binary[0] != '0' && bigint.Sign == 1)
    {
      base2.Append('0');
    }

    // Append binary string to StringBuilder.
    base2.Append(binary);

    // Convert remaining bytes adding leading zeros.
    for (idx--; idx >= 0; idx--)
    {
      base2.Append(Convert.ToString(bytes[idx], 2).PadLeft(8, '0'));
    }

    return base2.ToString();
  }

  /// <summary>
  /// Converts a <see cref="BigInteger"/> to a hexadecimal string.
  /// </summary>
  /// <param name="bigint">A <see cref="BigInteger"/>.</param>
  /// <returns>
  /// A <see cref="System.String"/> containing a hexadecimal
  /// representation of the supplied <see cref="BigInteger"/>.
  /// </returns>
  public static string ToHexadecimalString(this BigInteger bigint)
  {
    return bigint.ToString("X");
  }

  /// <summary>
  /// Converts a <see cref="BigInteger"/> to a octal string.
  /// </summary>
  /// <param name="bigint">A <see cref="BigInteger"/>.</param>
  /// <returns>
  /// A <see cref="System.String"/> containing an octal
  /// representation of the supplied <see cref="BigInteger"/>.
  /// </returns>
  public static string ToOctalString(this BigInteger bigint)
  {
    var bytes = bigint.ToByteArray();
    var idx = bytes.Length - 1;

    // Create a StringBuilder having appropriate capacity.
    var base8 = new StringBuilder(((bytes.Length / 3) + 1) * 8);

    // Calculate how many bytes are extra when byte array is split
    // into three-byte (24-bit) chunks.
    var extra = bytes.Length % 3;

    // If no bytes are extra, use three bytes for first chunk.
    if (extra == 0)
    {
      extra = 3;
    }

    // Convert first chunk (24-bits) to integer value.
    int int24 = 0;
    for (; extra != 0; extra--)
    {
      int24 <<= 8;
      int24 += bytes[idx--];
    }

    // Convert 24-bit integer to octal without adding leading zeros.
    var octal = Convert.ToString(int24, 8);

    // Ensure leading zero exists if value is positive.
    if (octal[0] != '0' && bigint.Sign == 1)
    {
      base8.Append('0');
    }

    // Append first converted chunk to StringBuilder.
    base8.Append(octal);

    // Convert remaining 24-bit chunks, adding leading zeros.
    for (; idx >= 0; idx -= 3)
    {
      int24 = (bytes[idx] << 16) + (bytes[idx - 1] << 8) + bytes[idx - 2];
      base8.Append(Convert.ToString(int24, 8).PadLeft(8, '0'));
    }

    return base8.ToString();
  }
}

На первый взгляд эти методы могут казаться более сложными, чем необходимо. На самом деле добавляется немного дополнительной сложности, чтобы обеспечить правильные ведущие нули в преобразованных строках.

Давайте рассмотрим каждый метод расширения, чтобы увидеть, как они работают:

BigInteger.ToBinaryString()

Вот как использовать этот метод расширения для преобразования BigInteger в двоичную строку:

// Convert BigInteger to binary string.
bigint.ToBinaryString();

Основным ядром каждого из этих методов расширения является метод BigInteger.ToByteArray(). Этот метод преобразует массив BigInteger в байтовый массив, так как мы можем получить двоичное представление значения BigInteger:

var bytes = bigint.ToByteArray();

Остерегайтесь, однако, возвращенный массив байтов находится в порядке порядка младшего порядка, поэтому первый элемент массива является младшим байтом (LSB) BigInteger. Поскольку a StringBuilder используется для построения выходной строки, которая начинается с самой значащей цифры (MSB) - , массив байтов должен быть повторен в обратном, так что самый старший байт сначала преобразован.

Таким образом, указательный указатель устанавливается на самую значимую цифру (последний элемент) в массиве байтов:

var idx = bytes.Length - 1;

Для захвата преобразованных байтов создается StringBuilder:

var base2 = new StringBuilder(bytes.Length * 8);

Конструктор StringBuilder вмещает StringBuilder. Емкость, необходимая для StringBuilder, вычисляется путем принятия количества байтов для преобразования, умноженного на восемь (восемь двоичных цифр, полученных от каждого преобразованного байта).

Затем первый байт преобразуется в двоичную строку:

var binary = Convert.ToString(bytes[idx], 2);

В этот момент необходимо обеспечить, чтобы начальный нуль существовал, если BigInteger является положительным значением (см. обсуждение выше). Если первая преобразованная цифра не равна нулю, а bigint положительна, то к StringBuilder добавляется a '0':

// Ensure leading zero exists if value is positive.
if (binary[0] != '0' && bigint.Sign == 1)
{
  base2.Append('0');
}

Затем преобразованный байт добавляется к StringBuilder:

base2.Append(binary);

Чтобы преобразовать оставшиеся байты, цикл повторяет оставшуюся часть массива байтов в обратном порядке:

for (idx--; idx >= 0; idx--)
{
  base16.Append(Convert.ToString(bytes[idx], 2).PadLeft(8, '0'));
}

Обратите внимание, что каждый преобразованный байт заполняется слева нулями ('0'), если необходимо, так что преобразованная строка состоит из восьми двоичных символов. Это очень важно. Без этого дополнения шестнадцатеричное значение "101" будет преобразовано в двоичное значение "11". Ведущие нули обеспечивают преобразование "100000001".

Когда все байты преобразуются, StringBuilder содержит полную двоичную строку, которая возвращается методом расширения:

return base2.ToString();

BigInteger.ToOctalString

Преобразование строки BigInteger в восьмеричную (базовую 8) более сложно. Проблема состоит в том, что восьмеричные цифры представляют собой три бита, которые не являются четными кратными восьми бит, содержащихся в каждом элементе массива байтов, созданного BigInteger.ToByteArray(). Чтобы решить эту проблему, три байта из массива объединены в куски 24-бит. Каждый 24-битный кусок равномерно преобразуется в восемь восьмеричных символов.

Первый 24-разрядный фрагмент требует некоторой модульной математики:

var extra = bytes.Length % 3;

Этот расчет определяет, сколько байтов является "дополнительным", когда весь массив байтов разбивается на трехбайтовые (24-битные) куски. Первое преобразование в восьмеричные (самые значащие цифры) получает "лишние" байты, так что все оставшиеся преобразования получат по три байта каждый.

Если "лишних" байтов нет, тогда первый кусок получает три байта:

if (extra == 0)
{
  extra = 3;
}

Первый фрагмент загружается в целочисленную переменную с именем int24, которая содержит до 24 бит. Загружается каждый байт блока. Когда загружаются дополнительные байты, предыдущие биты в int24 сдвигаются влево на 8 бит, чтобы освободить место:

int int24 = 0;
for (; extra != 0; extra--)
{
  int24 <<= 8;
  int24 += bytes[idx--];
}

Преобразование 24-битного фрагмента в восьмеричное выполняется с помощью:

var octal = Convert.ToString(int24, 8);

Опять же, первая цифра должна начинаться с нуля, если BigInteger - положительное значение:

// Ensure leading zero exists if value is positive.
if (octal[0] != '0' && bigint.Sign == 1)
{
  base8.Append('0');
}

Первый преобразованный фрагмент добавляется к StringBuilder:

base8.Append(octal);

Остальные 24-битные фрагменты преобразуются в цикле:

for (; idx >= 0; idx -= 3)
{
  int24 = (bytes[idx] << 16) + (bytes[idx -1] << 8) + bytes[idx - 2];
  base8.Append(Convert.ToString(int24, 8).PadLeft(8, '0'));
}

Как и двоичное преобразование, каждая преобразованная восьмеричная строка левая с нулями, так что "7" становится "00000007". Это гарантирует, что нули не будут удалены из середины преобразованных строк (т.е. "17" вместо "100000007" ).

Преобразование в базу x?

Преобразование a BigInteger в другие базы чисел может быть намного сложнее. Пока базовая цифра имеет мощность в два (то есть, 2, 4, 8, 16), массив байтов, созданный BigInteger.ToByteArray(), может быть соответствующим образом разбит на куски бит и преобразован.

Однако, если числовая база не является силой двух, проблема становится намного сложнее и требует много циклов и деления. Поскольку такие преобразования числовой базы являются редкими, я только рассмотрел популярные вычислительные базы здесь.

Ответ 2

После хорошего долгого рабочего дня с BigInteger я получил лучший способ сделать вывод строки в двоичном формате, попробуйте это! (работает для отрицательных чисел)

// Important note: when parsing hexadecimal string, make sure to prefix
// with 0 if the number is positive. Ex: 0F instead of F, and 01A3 instead of 1A3.
// If the number is negative, then the first bit should be set to 1.

var x = BigInteger.Parse("0F", NumberStyles.HexNumber); // Or: BigInteger.Parse("15")

var biBytes = x.ToByteArray();

var bits = new bool [8 * biBytes.Length];

new BitArray(x.ToByteArray()).CopyTo(bits, 0);

bits = bits.Reverse().ToArray(); // BigInteger uses little endian when extracting bytes (thus bits), so we inverse them.

var builder = new StringBuilder();

foreach(var bit in bits)
{
    builder.Append(bit ? '1' : '0');
}

string final = Regex.Replace(builder.ToString(), @"^0+", ""); // Because bytes consume full 8 bits, we might occasionally get leading zeros.

Console.WriteLine(final);

Выход: 1111

Ответ 3

Это простой способ преобразования BigInteger в любую базу:

public static string ToNBase(BigInteger a, int n)
        {
            StringBuilder sb = new StringBuilder();
            while (a > 0)
            {
                sb.Insert(0,a % n);
                a /= n;
            }
            return sb.ToString();
        }

Он отлично работает для основания 2-10. Если вы хотите, чтобы он создавал шестнадцатеричные или другие более высокие базовые строки, вам необходимо изменить форму a % b в соответствии с базой, прежде чем вставлять ее.