将基数为 10 的数字转换为 .NET 中任何基数的最快方法?
Posted
技术标签:
【中文标题】将基数为 10 的数字转换为 .NET 中任何基数的最快方法?【英文标题】:Quickest way to convert a base 10 number to any base in .NET? 【发布时间】:2010-10-29 18:07:20 【问题描述】:我有一个我写的旧的(ish)C#方法,它接受一个数字并将其转换为任何基数:
string ConvertToBase(int number, char[] baseChars);
这并不是超级快速和整洁。在 .NET 中是否有一种已知的好方法可以实现这一目标?
我正在寻找允许我使用 any 基数和任意字符串的东西。
这仅允许碱基 16、10、8 和 2:
Convert.ToString(1, x);
我想利用数字、全部小写和全部大写字母来利用它来实现非常高的基数。就像在 this thread 中一样,但对于 C# 而不是 javascript。
有人知道在 C# 中执行此操作的好方法吗?
【问题讨论】:
【参考方案1】:Convert.ToString
可用于将数字转换为其在指定基数中的等效字符串表示形式。
例子:
string binary = Convert.ToString(5, 2); // convert 5 to its binary representation
Console.WriteLine(binary); // prints 101
但是,正如 cmets 所指出的,Convert.ToString
仅支持以下有限但通常足够的基数集:2、8、10 或 16。
更新(以满足转换为任何基的要求):
我不知道 BCL 中有任何方法能够将数字转换为任何基数,因此您必须编写自己的小型实用程序函数。一个简单的示例看起来像这样(请注意,这肯定可以通过替换字符串连接来更快):
class Program
static void Main(string[] args)
// convert to binary
string binary = IntToString(42, new char[] '0', '1' );
// convert to hexadecimal
string hex = IntToString(42,
new char[] '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F');
// convert to hexavigesimal (base 26, A-Z)
string hexavigesimal = IntToString(42,
Enumerable.Range('A', 26).Select(x => (char)x).ToArray());
// convert to sexagesimal
string xx = IntToString(42,
new char[] '0','1','2','3','4','5','6','7','8','9',
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x');
public static string IntToString(int value, char[] baseChars)
string result = string.Empty;
int targetBase = baseChars.Length;
do
result = baseChars[value % targetBase] + result;
value = value / targetBase;
while (value > 0);
return result;
/// <summary>
/// An optimized method using an array as buffer instead of
/// string concatenation. This is faster for return values having
/// a length > 1.
/// </summary>
public static string IntToStringFast(int value, char[] baseChars)
// 32 is the worst cast buffer size for base 2 and int.MaxValue
int i = 32;
char[] buffer = new char[i];
int targetBase= baseChars.Length;
do
buffer[--i] = baseChars[value % targetBase];
value = value / targetBase;
while (value > 0);
char[] result = new char[32 - i];
Array.Copy(buffer, i, result, 0, 32 - i);
return new string(result);
更新 2(性能改进)
使用数组缓冲区而不是字符串连接来构建结果字符串可以提高性能,尤其是在大数字上(参见方法IntToStringFast
)。在最好的情况下(即可能的最长输入),这种方法大约***倍。但是,对于 1 位数字(即目标基数中的 1 位数字),IntToString
会更快。
【讨论】:
应该注意的是,这只支持基数 2,8,10,16 - 而不是问题中的“任何”。因为你永远不知道什么时候需要六十进制;-p 六进制听起来很有趣。 太棒了。但是反函数在哪里呢? :// 我这里有一个首过反函数:***.com/questions/3579970/… 对于“十六进制”转换(对于类似列的 excel),我必须将这一行result = baseChars[value % targetBase] + result;
替换为 result = baseChars[--value % targetBase] + result;
。我没有针对其他情况进行测试,因为我只需要从列号转换为 excel 列名。【参考方案2】:
this forum post 的这门课能帮到你吗?
public class BaseConverter
public static string ToBase(string number, int start_base, int target_base)
int base10 = this.ToBase10(number, start_base);
string rtn = this.FromBase10(base10, target_base);
return rtn;
public static int ToBase10(string number, int start_base)
if (start_base < 2 || start_base > 36) return 0;
if (start_base == 10) return Convert.ToInt32(number);
char[] chrs = number.ToCharArray();
int m = chrs.Length - 1;
int n = start_base;
int x;
int rtn = 0;
foreach(char c in chrs)
if (char.IsNumber(c))
x = int.Parse(c.ToString());
else
x = Convert.ToInt32(c) - 55;
rtn += x * (Convert.ToInt32(Math.Pow(n, m)));
m--;
return rtn;
public static string FromBase10(int number, int target_base)
if (target_base < 2 || target_base > 36) return "";
if (target_base == 10) return number.ToString();
int n = target_base;
int q = number;
int r;
string rtn = "";
while (q >= n)
r = q % n;
q = q / n;
if (r < 10)
rtn = r.ToString() + rtn;
else
rtn = Convert.ToChar(r + 55).ToString() + rtn;
if (q < 10)
rtn = q.ToString() + rtn;
else
rtn = Convert.ToChar(q + 55).ToString() + rtn;
return rtn;
完全未经测试...让我知道它是否有效! (复制粘贴以防论坛帖子消失或其他情况......)
【讨论】:
关闭.. 稍后我会玩。需要一些工作才能获取任何字符,但这是朝着正确方向迈出的一步。我将速度与我自己的方法进行比较! 如果你改进它记得分享它。其他人可能也想要 ot =) @joshcomley 周末过得怎么样? ;) 这是一个长周末 :D【参考方案3】:我使用它来将 Guid 存储为较短的字符串(但仅限于使用 106 个字符)。 如果有人对此感兴趣,是我将字符串解码回数值的代码(在这种情况下,我使用 2 ulongs 作为 Guid 值,而不是对 Int128 进行编码(因为我使用的是 3.5 而不是 4.0)。 为清楚起见,CODE 是一个具有 106 个唯一字符的字符串 const。 ConvertLongsToBytes 相当乏味。
private static Guid B106ToGuid(string pStr)
try
ulong tMutl = 1, tL1 = 0, tL2 = 0, targetBase = (ulong)CODE.Length;
for (int i = 0; i < pStr.Length / 2; i++)
tL1 += (ulong)CODE.IndexOf(pStr[i]) * tMutl;
tL2 += (ulong)CODE.IndexOf(pStr[pStr.Length / 2 + i]) * tMutl;
tMutl *= targetBase;
return new Guid(ConvertLongsToBytes(tL1, tL2));
catch (Exception ex)
throw new Exception("B106ToGuid failed to convert string to Guid", ex);
【讨论】:
【参考方案4】:我也在寻找一种将十进制数转换为 [2..36] 范围内的另一个基数的快速方法,因此我开发了以下代码。它易于遵循并使用 Stringbuilder 对象作为字符缓冲区的代理,我们可以逐个字符地索引该字符缓冲区。与替代方案相比,该代码似乎非常快,并且比初始化字符数组中的单个字符要快得多。
为了您自己的使用,您可能更喜欢: 1/ 返回一个空白字符串而不是抛出异常。 2/删除基数检查,使方法运行得更快 3/ 用 32 个 '0' 初始化 Stringbuilder 对象并删除行 result.Remove(0, i );。这将导致字符串返回前导零并进一步提高速度。 4/ 使 Stringbuilder 对象成为类中的静态字段,因此无论调用多少次 DecimalToBase 方法,Stringbuilder 对象都只会初始化一次。如果您执行此更改,上面的 3 将不再起作用。
我希望有人觉得这很有用:)
原子悖论
static string DecimalToBase(int number, int radix)
// Check that the radix is between 2 and 36 inclusive
if ( radix < 2 || radix > 36 )
throw new ArgumentException("ConvertToBase(int number, int radix) - Radix must be between 2 and 36.");
// Create a buffer large enough to hold the largest int value represented in binary digits
StringBuilder result = new StringBuilder(" "); // 32 spaces
// The base conversion calculates the digits in reverse order so use
// an index to point to the last unused space in our buffer
int i = 32;
// Convert the number to the new base
do
int remainder = number % radix;
number = number / radix;
if(remainder <= 9)
result[--i] = (char)(remainder + '0'); // Converts [0..9] to ASCII ['0'..'9']
else
result[--i] = (char)(remainder + '7'); // Converts [10..36] to ASCII ['A'..'Z']
while ( number > 0 );
// Remove the unwanted padding from the front of our buffer and return the result
// Note i points to the last unused character in our buffer
result.Remove( 0, i );
return (result.ToString());
【讨论】:
【参考方案5】:我也有类似的需求,除了我还需要对“数字”进行数学运算。我在这里接受了一些建议,并创建了一个类来完成所有这些有趣的事情。它允许使用任何 unicode 字符来表示一个数字,并且它也适用于小数。
这个类很容易使用。只需创建一个类型为New BaseNumber
的数字,设置一些属性,然后关闭即可。例程会自动在基数 10 和基数 x 之间切换,并且您设置的值保留在您设置的基数中,因此不会丢失准确性(直到转换,但即使这样,精度损失也应该非常小,因为这例程尽可能使用Double
和Long
。
我无法控制这个例程的速度。它可能很慢,所以我不确定它是否适合提出问题的人的需求,但它肯定是灵活的,所以希望其他人可以使用它。
对于可能需要此代码来计算 Excel 中的下一列的其他任何人,我将包含我使用的利用此类的循环代码。
Public Class BaseNumber
Private _CharacterArray As List(Of Char)
Private _BaseXNumber As String
Private _Base10Number As Double?
Private NumberBaseLow As Integer
Private NumberBaseHigh As Integer
Private DecimalSeparator As Char = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator
Private GroupSeparator As Char = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator
Public Sub UseCapsLetters()
'http://unicodelookup.com
TrySetBaseSet(65, 90)
End Sub
Public Function GetCharacterArray() As List(Of Char)
Return _CharacterArray
End Function
Public Sub SetCharacterArray(CharacterArray As String)
_CharacterArray = New List(Of Char)
_CharacterArray.AddRange(CharacterArray.ToList)
TrySetBaseSet(_CharacterArray)
End Sub
Public Sub SetCharacterArray(CharacterArray As List(Of Char))
_CharacterArray = CharacterArray
TrySetBaseSet(_CharacterArray)
End Sub
Public Sub SetNumber(Value As String)
_BaseXNumber = Value
_Base10Number = Nothing
End Sub
Public Sub SetNumber(Value As Double)
_Base10Number = Value
_BaseXNumber = Nothing
End Sub
Public Function GetBaseXNumber() As String
If _BaseXNumber IsNot Nothing Then
Return _BaseXNumber
Else
Return ToBaseString()
End If
End Function
Public Function GetBase10Number() As Double
If _Base10Number IsNot Nothing Then
Return _Base10Number
Else
Return ToBase10()
End If
End Function
Private Sub TrySetBaseSet(Values As List(Of Char))
For Each value As Char In _BaseXNumber
If Not Values.Contains(value) Then
Throw New ArgumentOutOfRangeException("The string has a value, " & value & ", not contained in the selected 'base' set.")
_CharacterArray.Clear()
DetermineNumberBase()
End If
Next
_CharacterArray = Values
End Sub
Private Sub TrySetBaseSet(LowValue As Integer, HighValue As Integer)
Dim HighLow As KeyValuePair(Of Integer, Integer) = GetHighLow()
If HighLow.Key < LowValue OrElse HighLow.Value > HighValue Then
Throw New ArgumentOutOfRangeException("The string has a value not contained in the selected 'base' set.")
_CharacterArray.Clear()
DetermineNumberBase()
End If
NumberBaseLow = LowValue
NumberBaseHigh = HighValue
End Sub
Private Function GetHighLow(Optional Values As List(Of Char) = Nothing) As KeyValuePair(Of Integer, Integer)
If Values Is Nothing Then
Values = _BaseXNumber.ToList
End If
Dim lowestValue As Integer = Convert.ToInt32(Values(0))
Dim highestValue As Integer = Convert.ToInt32(Values(0))
Dim currentValue As Integer
For Each value As Char In Values
If value <> DecimalSeparator AndAlso value <> GroupSeparator Then
currentValue = Convert.ToInt32(value)
If currentValue > highestValue Then
highestValue = currentValue
End If
If currentValue < lowestValue Then
currentValue = lowestValue
End If
End If
Next
Return New KeyValuePair(Of Integer, Integer)(lowestValue, highestValue)
End Function
Public Sub New(BaseXNumber As String)
_BaseXNumber = BaseXNumber
DetermineNumberBase()
End Sub
Public Sub New(BaseXNumber As String, NumberBase As Integer)
Me.New(BaseXNumber, Convert.ToInt32("0"c), NumberBase)
End Sub
Public Sub New(BaseXNumber As String, NumberBaseLow As Integer, NumberBaseHigh As Integer)
_BaseXNumber = BaseXNumber
Me.NumberBaseLow = NumberBaseLow
Me.NumberBaseHigh = NumberBaseHigh
End Sub
Public Sub New(Base10Number As Double)
_Base10Number = Base10Number
End Sub
Private Sub DetermineNumberBase()
Dim highestValue As Integer
Dim currentValue As Integer
For Each value As Char In _BaseXNumber
currentValue = Convert.ToInt32(value)
If currentValue > highestValue Then
highestValue = currentValue
End If
Next
NumberBaseHigh = highestValue
NumberBaseLow = Convert.ToInt32("0"c) 'assume 0 is the lowest
End Sub
Private Function ToBaseString() As String
Dim Base10Number As Double = _Base10Number
Dim intPart As Long = Math.Truncate(Base10Number)
Dim fracPart As Long = (Base10Number - intPart).ToString.Replace(DecimalSeparator, "")
Dim intPartString As String = ConvertIntToString(intPart)
Dim fracPartString As String = If(fracPart <> 0, DecimalSeparator & ConvertIntToString(fracPart), "")
Return intPartString & fracPartString
End Function
Private Function ToBase10() As Double
Dim intPartString As String = _BaseXNumber.Split(DecimalSeparator)(0).Replace(GroupSeparator, "")
Dim fracPartString As String = If(_BaseXNumber.Contains(DecimalSeparator), _BaseXNumber.Split(DecimalSeparator)(1), "")
Dim intPart As Long = ConvertStringToInt(intPartString)
Dim fracPartNumerator As Long = ConvertStringToInt(fracPartString)
Dim fracPartDenominator As Long = ConvertStringToInt(GetEncodedChar(1) & String.Join("", Enumerable.Repeat(GetEncodedChar(0), fracPartString.ToString.Length)))
Return Convert.ToDouble(intPart + fracPartNumerator / fracPartDenominator)
End Function
Private Function ConvertIntToString(ValueToConvert As Long) As String
Dim result As String = String.Empty
Dim targetBase As Long = GetEncodingCharsLength()
Do
result = GetEncodedChar(ValueToConvert Mod targetBase) & result
ValueToConvert = ValueToConvert \ targetBase
Loop While ValueToConvert > 0
Return result
End Function
Private Function ConvertStringToInt(ValueToConvert As String) As Long
Dim result As Long
Dim targetBase As Integer = GetEncodingCharsLength()
Dim startBase As Integer = GetEncodingCharsStartBase()
Dim value As Char
For x As Integer = 0 To ValueToConvert.Length - 1
value = ValueToConvert(x)
result += GetDecodedChar(value) * Convert.ToInt32(Math.Pow(GetEncodingCharsLength, ValueToConvert.Length - (x + 1)))
Next
Return result
End Function
Private Function GetEncodedChar(index As Integer) As Char
If _CharacterArray IsNot Nothing AndAlso _CharacterArray.Count > 0 Then
Return _CharacterArray(index)
Else
Return Convert.ToChar(index + NumberBaseLow)
End If
End Function
Private Function GetDecodedChar(character As Char) As Integer
If _CharacterArray IsNot Nothing AndAlso _CharacterArray.Count > 0 Then
Return _CharacterArray.IndexOf(character)
Else
Return Convert.ToInt32(character) - NumberBaseLow
End If
End Function
Private Function GetEncodingCharsLength() As Integer
If _CharacterArray IsNot Nothing AndAlso _CharacterArray.Count > 0 Then
Return _CharacterArray.Count
Else
Return NumberBaseHigh - NumberBaseLow + 1
End If
End Function
Private Function GetEncodingCharsStartBase() As Integer
If _CharacterArray IsNot Nothing AndAlso _CharacterArray.Count > 0 Then
Return GetHighLow.Key
Else
Return NumberBaseLow
End If
End Function
End Class
现在让代码循环遍历 Excel 列:
Public Function GetColumnList(DataSheetID As String) As List(Of String)
Dim workingColumn As New BaseNumber("A")
workingColumn.SetCharacterArray("@ABCDEFGHIJKLMNOPQRSTUVWXYZ")
Dim listOfPopulatedColumns As New List(Of String)
Dim countOfEmptyColumns As Integer
Dim colHasData As Boolean
Dim cellHasData As Boolean
Do
colHasData = True
cellHasData = False
For r As Integer = 1 To GetMaxRow(DataSheetID)
cellHasData = cellHasData Or XLGetCellValue(DataSheetID, workingColumn.GetBaseXNumber & r) <> ""
Next
colHasData = colHasData And cellHasData
'keep trying until we get 4 empty columns in a row
If colHasData Then
listOfPopulatedColumns.Add(workingColumn.GetBaseXNumber)
countOfEmptyColumns = 0
Else
countOfEmptyColumns += 1
End If
'we are already starting with column A, so increment after we check column A
Do
workingColumn.SetNumber(workingColumn.GetBase10Number + 1)
Loop Until Not workingColumn.GetBaseXNumber.Contains("@")
Loop Until countOfEmptyColumns > 3
Return listOfPopulatedColumns
End Function
您会注意到 Excel 部分的重要部分是 0 由重新编号中的 @ 标识。所以我只是过滤掉所有带有@的数字,然后得到正确的序列(A、B、C、...、Z、AA、AB、AC、...)。
【讨论】:
【参考方案6】:I recently blogged about this。我的实现在计算过程中不使用任何字符串操作,这使得它非常快。支持从 2 到 36 转换为任何数字系统:
/// <summary>
/// Converts the given decimal number to the numeral system with the
/// specified radix (in the range [2, 36]).
/// </summary>
/// <param name="decimalNumber">The number to convert.</param>
/// <param name="radix">The radix of the destination numeral system (in the range [2, 36]).</param>
/// <returns></returns>
public static string DecimalToArbitrarySystem(long decimalNumber, int radix)
const int BitsInLong = 64;
const string Digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (radix < 2 || radix > Digits.Length)
throw new ArgumentException("The radix must be >= 2 and <= " + Digits.Length.ToString());
if (decimalNumber == 0)
return "0";
int index = BitsInLong - 1;
long currentNumber = Math.Abs(decimalNumber);
char[] charArray = new char[BitsInLong];
while (currentNumber != 0)
int remainder = (int)(currentNumber % radix);
charArray[index--] = Digits[remainder];
currentNumber = currentNumber / radix;
string result = new String(charArray, index + 1, BitsInLong - index - 1);
if (decimalNumber < 0)
result = "-" + result;
return result;
我还实现了一个快速反函数,以防万一有人需要它: Arbitrary to Decimal Numeral System.
【讨论】:
我对这个页面上的所有解决方案都进行了性能测试,这是最快的,大约是最后短解决方案的两倍。 这是什么result = "-" + result
?那是某种填充物吗?我如何修改代码以便只使用 A-Z 或 0-9 作为填充字符?
result = "-" + result
中的"-"
代表负数的负号。这不是填充字符。
你可以取消字符串 concat 如果你把'-'放到index
的charArray中,然后在你创建一个新字符串之前减少索引【参考方案7】:
参加这个聚会很晚,但我最近为工作中的一个项目编写了以下帮助程序类。它旨在将短字符串转换为数字并再次转换回来(一个简单的perfect hash 函数),但它也将在任意基数之间执行数字转换。 Base10ToString
方法实现回答了最初发布的问题。
需要传递给类构造函数的shouldSupportRoundTripping
标志,以防止在转换为base-10 并再次返回期间丢失数字字符串中的前导数字(鉴于我的要求,这很重要!)。大多数情况下,数字字符串中前导 0 的丢失可能不会成为问题。
不管怎样,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ***
/// <summary>
/// Contains methods used to convert numbers between base-10 and another numbering system.
/// </summary>
/// <remarks>
/// <para>
/// This conversion class makes use of a set of characters that represent the digits used by the target
/// numbering system. For example, binary would use the digits 0 and 1, whereas hex would use the digits
/// 0 through 9 plus A through F. The digits do not have to be numerals.
/// </para>
/// <para>
/// The first digit in the sequence has special significance. If the number passed to the
/// <see cref="StringToBase10"/> method has leading digits that match the first digit, then those leading
/// digits will effectively be 'lost' during conversion. Much of the time this won't matter. For example,
/// "0F" hex will be converted to 15 decimal, but when converted back to hex it will become simply "F",
/// losing the leading "0". However, if the set of digits was A through Z, and the number "ABC" was
/// converted to base-10 and back again, then the leading "A" would be lost. The <see cref="System.Boolean"/>
/// flag passed to the constructor allows 'round-tripping' behaviour to be supported, which will prevent
/// leading digits from being lost during conversion.
/// </para>
/// <para>
/// Note that numeric overflow is probable when using longer strings and larger digit sets.
/// </para>
/// </remarks>
public class Base10Converter
const char NullDigit = '\0';
public Base10Converter(string digits, bool shouldSupportRoundTripping = false)
: this(digits.ToCharArray(), shouldSupportRoundTripping)
public Base10Converter(IEnumerable<char> digits, bool shouldSupportRoundTripping = false)
if (digits == null)
throw new ArgumentNullException("digits");
if (digits.Count() == 0)
throw new ArgumentException(
message: "The sequence is empty.",
paramName: "digits"
);
if (!digits.Distinct().SequenceEqual(digits))
throw new ArgumentException(
message: "There are duplicate characters in the sequence.",
paramName: "digits"
);
if (shouldSupportRoundTripping)
digits = (new[] NullDigit ).Concat(digits);
_digitToIndexMap =
digits
.Select((digit, index) => new digit, index )
.ToDictionary(keySelector: x => x.digit, elementSelector: x => x.index);
_radix = _digitToIndexMap.Count;
_indexToDigitMap =
_digitToIndexMap
.ToDictionary(keySelector: x => x.Value, elementSelector: x => x.Key);
readonly Dictionary<char, int> _digitToIndexMap;
readonly Dictionary<int, char> _indexToDigitMap;
readonly int _radix;
public long StringToBase10(string number)
Func<char, int, long> selector =
(c, i) =>
int power = number.Length - i - 1;
int digitIndex;
if (!_digitToIndexMap.TryGetValue(c, out digitIndex))
throw new ArgumentException(
message: String.Format("Number contains an invalid digit '0' at position 1.", c, i),
paramName: "number"
);
return Convert.ToInt64(digitIndex * Math.Pow(_radix, power));
;
return number.Select(selector).Sum();
public string Base10ToString(long number)
if (number < 0)
throw new ArgumentOutOfRangeException(
message: "Value cannot be negative.",
paramName: "number"
);
string text = string.Empty;
long remainder;
do
number = Math.DivRem(number, _radix, out remainder);
char digit;
if (!_indexToDigitMap.TryGetValue((int) remainder, out digit) || digit == NullDigit)
throw new ArgumentException(
message: "Value cannot be converted given the set of digits used by this converter.",
paramName: "number"
);
text = digit + text;
while (number > 0);
return text;
这也可以被子类化以派生自定义数字转换器:
namespace ***
public sealed class BinaryNumberConverter : Base10Converter
public BinaryNumberConverter()
: base(digits: "01", shouldSupportRoundTripping: false)
public sealed class HexNumberConverter : Base10Converter
public HexNumberConverter()
: base(digits: "0123456789ABCDEF", shouldSupportRoundTripping: false)
代码会这样使用:
using System.Diagnostics;
namespace ***
class Program
static void Main(string[] args)
var converter = new Base10Converter(
digits: "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz",
shouldSupportRoundTripping: true
);
long number = converter.StringToBase10("Atoz");
string text = converter.Base10ToString(number);
Debug.Assert(text == "Atoz");
var converter = new HexNumberConverter();
string text = converter.Base10ToString(255);
long number = converter.StringToBase10(text);
Debug.Assert(number == 255);
【讨论】:
【参考方案8】:也可以使用已接受版本的略微修改版本,并根据需要调整基本字符串:
public static string Int32ToString(int value, int toBase)
string result = string.Empty;
do
result = "0123456789ABCDEF"[value % toBase] + result;
value /= toBase;
while (value > 0);
return result;
【讨论】:
【参考方案9】:快速“FROM”和“TO”方法
我迟到了,但我将之前的答案复合并改进了它们。我认为这两种方法比迄今为止发布的任何其他方法都快。我能够在单核机器上在不到 400 毫秒的时间内将 1,000,000 个数字从 36 进制转换为 36 进制。
以下示例适用于 base 62。更改 BaseChars
数组以在任何其他基础之间进行转换。
private static readonly char[] BaseChars =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray();
private static readonly Dictionary<char, int> CharValues = BaseChars
.Select((c,i)=>new Char=c, Index=i)
.ToDictionary(c=>c.Char,c=>c.Index);
public static string LongToBase(long value)
long targetBase = BaseChars.Length;
// Determine exact number of characters to use.
char[] buffer = new char[Math.Max(
(int) Math.Ceiling(Math.Log(value + 1, targetBase)), 1)];
var i = buffer.Length;
do
buffer[--i] = BaseChars[value % targetBase];
value = value / targetBase;
while (value > 0);
return new string(buffer, i, buffer.Length - i);
public static long BaseToLong(string number)
char[] chrs = number.ToCharArray();
int m = chrs.Length - 1;
int n = BaseChars.Length, x;
long result = 0;
for (int i = 0; i < chrs.Length; i++)
x = CharValues[ chrs[i] ];
result += x * (long)Math.Pow(n, m--);
return result;
编辑 (2018-07-12)
修复了@AdrianBotor(参见 cmets)发现的将 46655 转换为基数 36 的极端情况。这是由计算 Math.Log(46656, 36)
的小浮点错误引起的,该错误恰好是 3,但 .NET 返回 3 + 4.44e-16
,这会导致输出缓冲区中出现额外字符。
【讨论】:
@AdrianBotor 无法重现您的问题:BaseToLong(LongToBase(46655)) == 46655
@Diego,抱歉回复晚了。让我们用0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ
初始化数组并转换值46655
。结果应该是ZZZ
,但在调试器中我得到\0ZZZ
。只有这个值才能获得额外的\0
。例如值46654
正确转换为ZZY
。
@AdrianBotor 很好。通过将 LongToBase
中的 return 语句调整为 return new string(buffer, (int) i, buffer.Length - (int)i);
来解决【参考方案10】:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConvertToAnyBase
class Program
static void Main(string[] args)
var baseNumber = int.Parse(Console.ReadLine());
var number = int.Parse(Console.ReadLine());
string conversion = "";
while(number!=0)
conversion += Convert.ToString(number % baseNumber);
number = number / baseNumber;
var conversion2 = conversion.ToArray().Reverse();
Console.WriteLine(string.Join("", conversion2));
【讨论】:
这是从 1 到 10 的基数。【参考方案11】:这是基于 Pavel 的回答,但取消了他不必要的负数字符串连接。基数也是由传入的字符定义的,所以如果你想使用字符 ABC 将数字转换为基数 3,请传递“ABC” - 3 的字符串长度是基数:
C#
public static string ToBase(long base10, string baseChars = "0123456789ABCDEFGHIJKLMNOPQRTSUVWXYZ")
if (baseChars.Length < 2)
throw new ArgumentException("baseChars must be at least 2 chars long");
if (base10 == 0L)
return baseChars[0].ToString();
bool isNegative = base10 < 0L;
int radix = baseChars.Length;
int index = 64; // because it's how long a string will be if the basechars are 2 long (binary)
var chars = new char[index + 1]; // 65 chars, 64 from above plus one for sign if it's negative
base10 = Math.Abs(base10);
while (base10 > 0L)
chars[index] = baseChars[(int)(base10 % radix)];
base10 /= radix;
index--;
if (isNegative)
chars[index] = '-';
index--;
return new string(chars, index + 1, chars.Length - index - 1);
VB.NET
Public Shared Function ToBase(base10 As Long, Optional baseChars As String = "0123456789ABCDEFGHIJKLMNOPQRTSUVWXYZ") As String
If baseChars.Length < 2 Then Throw New ArgumentException("baseChars must be at least 2 chars long")
If base10 = 0 Then Return baseChars(0)
Dim isNegative = base10 < 0
Dim radix = baseChars.Length
Dim index As Integer = 64 'because it's how long a string will be if the basechars are 2 long (binary)
Dim chars(index) As Char '65 chars, 64 from above plus one for sign if it's negative
base10 = Math.Abs(base10)
While base10 > 0
chars(index) = baseChars(base10 Mod radix)
base10 \= radix
index -= 1
End While
If isNegative Then
chars(index) = "-"c
index -= 1
End If
Return New String(chars, index + 1, UBound(chars) - index)
End Function
【讨论】:
【参考方案12】:这是一种相当简单的方法,但它可能不是最快的。它非常强大,因为它是可组合的。
public static IEnumerable<int> ToBase(this int x, int b)
IEnumerable<int> ToBaseReverse()
if (x == 0)
yield return 0;
yield break;
int z = x;
while (z > 0)
yield return z % b;
z = z / b;
return ToBaseReverse().Reverse();
将它与这个简单的扩展方法结合起来,现在可以得到任何基础:
public static string ToBase(this int number, string digits) =>
String.Concat(number.ToBase(digits.Length).Select(x => digits[x]));
可以这样使用:
var result = 23.ToBase("01");
var result2 = 23.ToBase("012X");
Console.WriteLine(result);
Console.WriteLine(result2);
输出是:
10111 11X【讨论】:
以上是关于将基数为 10 的数字转换为 .NET 中任何基数的最快方法?的主要内容,如果未能解决你的问题,请参考以下文章