Click here to Skip to main content
15,887,676 members
Articles / Programming Languages / C#
Article

Base 36 type for .NET (C#)

Rate me:
Please Sign up or sign in to vote.
3.32/5 (31 votes)
7 Jun 20051 min read 163.3K   3.9K   25   32
A struct that provides base-36 functionality.

Introduction

I found myself wanting to express a whole host of numbers in as few characters as possible. I came up with the idea of using a Base36 type to represent Base 36 numbers. This scheme is basically an extension to hexadecimal, but whereas hexadecimal stops at 15 (F), Base 36 carries on, with G being 16, all the way up to Z, which is 35. 10 in Base 36 is in fact 36 in Base 10. Base 36 has the benefit that the "numbers" are expressed by characters that are readable to humans, so this can be a good way of passing numerical data over the telephone for instance. Using Base 36, numbers up to 46,655 can be expressed using only 3 characters (ZZZ).

The Code

The code for my Base36 struct is extremely simple, so I won't go into details explaining it; download it and take a look. I've overloaded as many operators as I could, so Base 36 numbers can be added, subtracted, multiplied etc... I've tried to keep the methods of my struct consistent with the way that Microsoft labels type methods. You can instantiate a Base 36 "number" in string format, or from a standard Base 10 number:

C#
Base36 b1 = 104;
//This has the value 104 in base 10.

Base36 b2 = "DSGFDFDZ434";
//This has the value 50,420,080,957,151,344 in Base 10.

The source files should be made into a class library. The demo project should be made into a console application, with a reference added to the class library; this demonstrates my struct in action!

And that's it! The struct seems to work very well, although I'm sure some of the code can be optimised. Good luck!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United Kingdom United Kingdom
I left Nottingham University in 2000 with an MSci in Mathematical Physics proudly in my grasp... but not a clue what I wanted to do with it!

Somewhere along the way however, I managed to swap Quantum Mechanics and General Relativity for RecordSets and Arrays, and took on some freelance VB development work. I soon realised I'd found my vocation, and promptly found an IT post in Nottingham, where I began developing Access databases (using VBA) for local government offices.

At some point I moved from Access to SQL Server, and before long, got my grubby palms on a copy of Visual Studio .NET. A very important milestone in my development career I think! Armed with my new found .NET knowledge, and with four years IT experience under my belt, I found my second job as Project Manager/IT Systems Developer, which brings us nicely up to date. I now work almost exclusively in .NET, writing mainly C# Windows applications and the odd web service, although I’m working on my ASP.NET too: the future of software development.

Comments and Discussions

 
QuestionLicense of this code Pin
hugob12310-Sep-13 23:18
hugob12310-Sep-13 23:18 
AnswerRe: License of this code Pin
Steve Barker 33311-Sep-13 9:57
Steve Barker 33311-Sep-13 9:57 
GeneralGreat Stuff! Pin
mikemkii14-Oct-11 5:06
mikemkii14-Oct-11 5:06 
GeneralBase 30 Derivative Class Pin
Tyler Jensen24-Nov-08 13:48
Tyler Jensen24-Nov-08 13:48 
GeneralI can't find the Exception Classes in the source code Pin
james.wren30-Jun-08 5:54
james.wren30-Jun-08 5:54 
GeneralRe: I can't find the Exception Classes in the source code Pin
Steve Barker 33330-Jun-08 12:17
Steve Barker 33330-Jun-08 12:17 
GeneralRe: I can't find the Exception Classes in the source code Pin
Steve Barker 33330-Jun-08 12:26
Steve Barker 33330-Jun-08 12:26 
...in fact, I can't see an obvious way to add new source code, so I'll post the code here.

Here's an update version of Base-36:
<code>
/// <summary>
/// Class representing a Base36 number
/// </summary>
public struct Base36
{
#region Constants (and pseudo-constants)

/// <summary>
/// Base36 containing the maximum supported value for this type
/// </summary>
public static readonly Base36 MaxValue = new Base36(long.MaxValue);
/// <summary>
/// Base36 containing the minimum supported value for this type
/// </summary>
public static readonly Base36 MinValue = new Base36(long.MinValue + 1);

#endregion

#region Fields

private long numericValue;

#endregion

#region Constructor

/// <summary>
/// Instantiate a Base36 number from a long value
/// </summary>
/// <param name="NumericValue">The long value to give to the Base36 number</param>
public Base36(long NumericValue)
{
numericValue = 0; //required by the struct.
this.NumericValue = NumericValue;
}


/// <summary>
/// Instantiate a Base36 number from a Base36 string
/// </summary>
/// <param name="Value">The value to give to the Base36 number</param>
public Base36(string Value)
{
numericValue = 0; //required by the struct.
this.Value = Value;
}


#endregion

#region Properties

/// <summary>
/// Get or set the value of the type using a base-10 long integer
/// </summary>
public long NumericValue
{
get
{
return numericValue;
}
set
{
numericValue = value;
}
}


/// <summary>
/// Get or set the value of the type using a Base36 string
/// </summary>
public string Value
{
get
{
return Base36.NumberToBase36(numericValue);
}
set
{
try
{
numericValue = Base36.Base36ToNumber(value);
}
catch
{
//Catch potential errors
throw new InvalidBase36StringException(value);
}
}
}


#endregion

#region Public Static Methods

/// <summary>
/// Static method to convert a Base36 string to a long integer (base-10)
/// </summary>
/// <param name="Base36Value">The number to convert from</param>
/// <returns>The long integer</returns>
public static long Base36ToNumber(string Base36Value)
{
//Make sure we have passed something
if(Base36Value == "")
{
throw new InvalidBase36StringException(Base36Value);
}

//Make sure the number is in upper case:
Base36Value = Base36Value.ToUpper();

//Account for negative values:
bool isNegative = false;

if(Base36Value[0] == '-')
{
Base36Value = Base36Value.Substring(1);
isNegative = true;
}

//Loop through our string and calculate its value
try
{
//Keep a running total of the value
long returnValue = Base36DigitToNumber(Base36Value[Base36Value.Length - 1]);

//Loop through the character in the string (right to left) and add
//up increasing powers as we go.
for(int i = 1; i < Base36Value.Length; i++)
{
returnValue += ((long)Math.Pow(36, i) * Base36DigitToNumber(Base36Value[Base36Value.Length - (i + 1)]));
}

//Do negative correction if required:
if(isNegative)
{
return returnValue * -1;
}
else
{
return returnValue;
}
}
catch
{
//If something goes wrong, this is not a valid number
throw new InvalidBase36StringException(Base36Value);
}
}


/// <summary>
/// Public static method to convert a long integer (base-10) to a Base36 number
/// </summary>
/// <param name="NumericValue">The base-10 long integer</param>
/// <returns>A Base36 representation</returns>
public static string NumberToBase36(long NumericValue)
{
try
{
//Handle negative values:
if(NumericValue < 0)
{
return string.Concat("-", PositiveNumberToBase36(Math.Abs(NumericValue)));
}
else
{
return PositiveNumberToBase36(NumericValue);
}
}
catch
{
throw new InvalidBase36NumberException(NumericValue);
}
}


#endregion

#region Private Static Methods

private static string PositiveNumberToBase36(long NumericValue)
{
//This is a clever recursively called function that builds
//the base-36 string representation of the long base-10 value
if(NumericValue < 36)
{
//The get out clause; fires when we reach a number less than
//36 - this means we can add the last digit.
return NumberToBase36Digit((byte)NumericValue).ToString();
}
else
{
//Add digits from left to right in powers of 36
//(recursive)
return string.Concat(PositiveNumberToBase36(NumericValue / 36), NumberToBase36Digit((byte)(NumericValue % 36)).ToString());
}
}


private static byte Base36DigitToNumber(char Base36Digit)
{
//Converts one base-36 digit to it's base-10 value
if(!char.IsLetterOrDigit(Base36Digit))
{
throw new InvalidBase36CharacterValueException(Base36Digit);
}

if(char.IsDigit(Base36Digit))
{
//Handles 0 - 9
return byte.Parse(Base36Digit.ToString());
}
else
{
//Handles A - Z
return (byte)((int)Base36Digit - 55);
}
}


private static char NumberToBase36Digit(byte NumericValue)
{
//Converts a number to it's base-36 value.
//Only works for numbers <= 35.
if(NumericValue > 35)
{
throw new InvalidBase36DigitValueException(NumericValue);
}

//Numbers:
if(NumericValue <= 9)
{
return NumericValue.ToString()[0];
}
else
{
//Note that A is code 65, and in this
//scheme, A = 10.
return (char)(NumericValue + 55);
}
}


#endregion

#region Operator Overloads

/// <summary>
/// Operator overload
/// </summary>
/// <param name="LHS"></param>
/// <param name="RHS"></param>
/// <returns></returns>
public static bool operator > (Base36 LHS, Base36 RHS)
{
return LHS.numericValue > RHS.numericValue;
}


/// <summary>
/// Operator overload
/// </summary>
/// <param name="LHS"></param>
/// <param name="RHS"></param>
/// <returns></returns>
public static bool operator < (Base36 LHS, Base36 RHS)
{
return LHS.numericValue < RHS.numericValue;
}


/// <summary>
/// Operator overload
/// </summary>
/// <param name="LHS"></param>
/// <param name="RHS"></param>
/// <returns></returns>
public static bool operator >= (Base36 LHS, Base36 RHS)
{
return LHS.numericValue >= RHS.numericValue;
}


/// <summary>
/// Operator overload
/// </summary>
/// <param name="LHS"></param>
/// <param name="RHS"></param>
/// <returns></returns>
public static bool operator <= (Base36 LHS, Base36 RHS)
{
return LHS.numericValue <= RHS.numericValue;
}


/// <summary>
/// Operator overload
/// </summary>
/// <param name="LHS"></param>
/// <param name="RHS"></param>
/// <returns></returns>
public static bool operator == (Base36 LHS, Base36 RHS)
{
return LHS.numericValue == RHS.numericValue;
}


/// <summary>
/// Operator overload
/// </summary>
/// <param name="LHS"></param>
/// <param name="RHS"></param>
/// <returns></returns>
public static bool operator != (Base36 LHS, Base36 RHS)
{
return !(LHS == RHS);
}


/// <summary>
/// Operator overload
/// </summary>
/// <param name="LHS"></param>
/// <param name="RHS"></param>
/// <returns></returns>
public static Base36 operator + (Base36 LHS, Base36 RHS)
{
return new Base36(LHS.numericValue + RHS.numericValue);
}


/// <summary>
/// Operator overload
/// </summary>
/// <param name="LHS"></param>
/// <param name="RHS"></param>
/// <returns></returns>
public static Base36 operator - (Base36 LHS, Base36 RHS)
{
return new Base36(LHS.numericValue - RHS.numericValue);
}


/// <summary>
/// Operator overload
/// </summary>
/// <param name="Value"></param>
/// <returns></returns>
public static Base36 operator ++ (Base36 Value)
{
return new Base36(Value.numericValue++);
}


/// <summary>
/// Operator overload
/// </summary>
/// <param name="Value"></param>
/// <returns></returns>
public static Base36 operator -- (Base36 Value)
{
return new Base36(Value.numericValue--);
}


/// <summary>
/// Operator overload
/// </summary>
/// <param name="LHS"></param>
/// <param name="RHS"></param>
/// <returns></returns>
public static Base36 operator * (Base36 LHS, Base36 RHS)
{
return new Base36(LHS.numericValue * RHS.numericValue);
}


/// <summary>
/// Operator overload
/// </summary>
/// <param name="LHS"></param>
/// <param name="RHS"></param>
/// <returns></returns>
public static Base36 operator / (Base36 LHS, Base36 RHS)
{
return new Base36(LHS.numericValue / RHS.numericValue);
}


/// <summary>
/// Operator overload
/// </summary>
/// <param name="LHS"></param>
/// <param name="RHS"></param>
/// <returns></returns>
public static Base36 operator % (Base36 LHS, Base36 RHS)
{
return new Base36(LHS.numericValue % RHS.numericValue);
}


/// <summary>
/// Converts type Base36 to a base-10 long
/// </summary>
/// <param name="Value">The Base36 object</param>
/// <returns>The base-10 long integer</returns>
public static implicit operator long (Base36 Value)
{
return Value.numericValue;
}


/// <summary>
/// Converts type Base36 to a base-10 integer
/// </summary>
/// <param name="Value">The Base36 object</param>
/// <returns>The base-10 integer</returns>
public static implicit operator int (Base36 Value)
{
try
{
return (int)Value.numericValue;
}
catch
{
throw new OverflowException("Overflow: Value too large to return as an integer");
}
}


/// <summary>
/// Converts type Base36 to a base-10 short
/// </summary>
/// <param name="Value">The Base36 object</param>
/// <returns>The base-10 short</returns>
public static implicit operator short (Base36 Value)
{
try
{
return (short)Value.numericValue;
}
catch
{
throw new OverflowException("Overflow: Value too large to return as a short");
}
}


/// <summary>
/// Converts a long (base-10) to a Base36 type
/// </summary>
/// <param name="Value">The long to convert</param>
/// <returns>The Base36 object</returns>
public static implicit operator Base36 (long Value)
{
return new Base36(Value);
}


/// <summary>
/// Converts type Base36 to a string; must be explicit, since
/// Base36 > string is dangerous!
/// </summary>
/// <param name="Value">The Base36 type</param>
/// <returns>The string representation</returns>
public static explicit operator string (Base36 Value)
{
return Value.Value;
}


/// <summary>
/// Converts a string to a Base36
/// </summary>
/// <param name="Value">The string (must be a Base36 string)</param>
/// <returns>A Base36 type</returns>
public static implicit operator Base36 (string Value)
{
return new Base36(Value);
}


#endregion

#region Public Override Methods

/// <summary>
/// Returns a string representation of the Base36 number
/// </summary>
/// <returns>A string representation</returns>
public override string ToString()
{
return Base36.NumberToBase36(numericValue);
}


/// <summary>
/// A unique value representing the value of the number
/// </summary>
/// <returns>The unique number</returns>
public override int GetHashCode()
{
return numericValue.GetHashCode();
}


/// <summary>
/// Determines if an object has the same value as the instance
/// </summary>
/// <param name="obj">The object to compare</param>
/// <returns>True if the values are the same</returns>
public override bool Equals(object obj)
{
if(!(obj is Base36))
{
return false;
}
else
{
return this == (Base36)obj;
}
}


#endregion

#region Public Methods

/// <summary>
/// Returns a string representation padding the leading edge with
/// zeros if necessary to make up the number of characters
/// </summary>
/// <param name="MinimumDigits">The minimum number of digits that the string must contain</param>
/// <returns>The padded string representation</returns>
public string ToString(int MinimumDigits)
{
string base36Value = Base36.NumberToBase36(numericValue);

if(base36Value.Length >= MinimumDigits)
{
return base36Value;
}
else
{
string padding = new string('0', (MinimumDigits - base36Value.Length));
return string.Format("{0}{1}", padding, base36Value);
}
}


#endregion

}
</code>

...and all the exceptions you'll need:

<code>
public class InvalidBase36CharacterValueException : Exception
{
#region Private Fields

private char value;

#endregion

#region Internal Constructor

internal InvalidBase36CharacterValueException(char value)
{
this.value = value;
}

#endregion

#region Public Override Methods

public override string Message
{
get
{
return string.Format("The character {0} is not a valid base-36 character", value);
}
}

#endregion
}
</code>

<code>
public class InvalidBase36DigitValueException : Exception
{
#region Private Fields

private byte value;

#endregion

#region Internal Constructor

internal InvalidBase36DigitValueException(byte value)
{
this.value = value;
}

#endregion

#region Public Override Methods

public override string Message
{
get
{
return string.Format("The value {0} could not be converted to a single base-36 digit", value);
}
}

#endregion
}
</code>

<code>
public class InvalidBase36NumberException : Exception
{
#region Private Fields

private long value;

#endregion

#region Internal Constructor

internal InvalidBase36NumberException(long value)
{
this.value = value;
}

#endregion

#region Public Override Methods

public override string Message
{
get
{
return string.Format("The number {0} could not be converted to a valid base-36 string", value);
}
}

#endregion
}
</code>

<code>
public class InvalidBase36StringException : Exception
{
#region Private Fields

private string value;

#endregion

#region Internal Constructor

internal InvalidBase36StringException(string value)
{
this.value = value;
}

#endregion

#region Public Override Methods

public override string Message
{
get
{
return string.Format("The string {0} is not a valid base-36 number", value);
}
}

#endregion
}
</code>

I hope that helps?

Cheers,

Steve.

Steve Barker
GeneralRe: I can't find the Exception Classes in the source code Pin
jweinraub16-Mar-11 6:10
jweinraub16-Mar-11 6:10 
GeneralTesting Code for Base36ToUInt64() Pin
Russell Mangel23-Mar-06 22:00
Russell Mangel23-Mar-06 22:00 
GeneralTesting Code for UInt64ToBase36() Pin
Russell Mangel23-Mar-06 21:55
Russell Mangel23-Mar-06 21:55 
GeneralHere is my version of Base36 Pin
Russell Mangel23-Mar-06 21:53
Russell Mangel23-Mar-06 21:53 
GeneralRe: Here is my version of Base36 Pin
samyu1*23-Jun-09 5:57
samyu1*23-Jun-09 5:57 
GeneralRe: Here is my version of Base36 Pin
Steve Barker 33324-Jun-09 11:46
Steve Barker 33324-Jun-09 11:46 
GeneralRe: Here is my version of Base36 Pin
samyu1*25-Jun-09 3:48
samyu1*25-Jun-09 3:48 
GeneralRe: Here is my version of Base36 Pin
Steve Barker 33325-Jun-09 6:21
Steve Barker 33325-Jun-09 6:21 
Questionwhat about this... Pin
Jeremy Falcon8-Jun-05 9:46
professionalJeremy Falcon8-Jun-05 9:46 
AnswerRe: what about this... Pin
Steve Barker 33312-Jun-05 4:30
Steve Barker 33312-Jun-05 4:30 
GeneralRe: what about this... Pin
Jeremy Falcon12-Jun-05 15:35
professionalJeremy Falcon12-Jun-05 15:35 
GeneralFantastic! Pin
GeminiMan7-Jun-05 17:41
GeminiMan7-Jun-05 17:41 
GeneralRe: Fantastic! Pin
tupacs018-Jun-05 4:50
tupacs018-Jun-05 4:50 
GeneralRe: Fantastic! Pin
umuhk8-Jun-05 8:47
umuhk8-Jun-05 8:47 
GeneralRe: Fantastic! Pin
Keith Farmer8-Jun-05 8:55
Keith Farmer8-Jun-05 8:55 
GeneralRe: Fantastic! Pin
Steve Barker 33312-Jun-05 4:34
Steve Barker 33312-Jun-05 4:34 
GeneralRe: Fantastic! Pin
tupacs0112-Jun-05 8:03
tupacs0112-Jun-05 8:03 
GeneralRe: Fantastic! Pin
Jeremy Falcon8-Jun-05 9:51
professionalJeremy Falcon8-Jun-05 9:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.