How do I convert an Int to a String in C# without using ToString()?
C#StringAlgorithmIntC# Problem Overview
>Convert the following int argument into a string without using any native toString functionality.
>
public string integerToString(int integerPassedIn){
//Your code here
}
Since everything inherits from Object
and Object
has a ToString()
method how would you convert an int
to a string
without using the native ToString()
method?
The problem with string concatenation is that it will call ToString()
up the chain until it hits one or hits the Object
class.
How do you convert an integer to a string in C# without using ToString()
?
C# Solutions
Solution 1 - C#
Something like this:
public string IntToString(int a)
{
var chars = new[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
var str = string.Empty;
if (a == 0)
{
str = chars[0];
}
else if (a == int.MinValue)
{
str = "-2147483648";
}
else
{
bool isNegative = (a < 0);
if (isNegative)
{
a = -a;
}
while (a > 0)
{
str = chars[a % 10] + str;
a /= 10;
}
if (isNegative)
{
str = "-" + str;
}
}
return str;
}
Update: Here's another version which is shorter and should perform much better, since it eliminates all string concatenation in favor of manipulating a fixed-length array. It supports bases up to 16, but it would be easy to extend it to higher bases. It could probably be improved further:
public string IntToString(int a, int radix)
{
var chars = "0123456789ABCDEF".ToCharArray();
var str = new char[32]; // maximum number of chars in any base
var i = str.Length;
bool isNegative = (a < 0);
if (a <= 0) // handles 0 and int.MinValue special cases
{
str[--i] = chars[-(a % radix)];
a = -(a / radix);
}
while (a != 0)
{
str[--i] = chars[a % radix];
a /= radix;
}
if (isNegative)
{
str[--i] = '-';
}
return new string(str, i, str.Length - i);
}
Solution 2 - C#
This is the solution I always use:
public static string numberBaseChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static string IntToStringWithBase(int n, int b) {
return IntToStringWithBase(n, b, 1);
}
public static string IntToStringWithBase(int n, int b, int minDigits) {
if (minDigits < 1) minDigits = 1;
if (n == 0) return new string('0', minDigits);
string s = "";
if ((b < 2) || (b > numberBaseChars.Length)) return s;
bool neg = false;
if ((b == 10) && (n < 0)) { neg = true; n = -n; }
uint N = (uint)n;
uint B = (uint)b;
while ((N > 0) | (minDigits-- > 0)) {
s = numberBaseChars[(int)(N % B)] + s;
N /= B;
}
if (neg) s = "-" + s;
return s;
}
This looks quite complicated but has the following features:
- Supports radix 2 to 36
- Handles negative values
- Optional total number of digits
Solution 3 - C#
I am not truly convinced the concatenation operator +
calls ToString
, but if that is indeed true, you can avoid those two by doing something like the following:
if (a == 0) return "0";
/* Negative maxint doesn't have a corresponding positive value, so handle it
* as a special case. Thanks to @Daniel for pointing this out.
*/
if (a == 0x80000000) return "-2147483648";
List<char> l = new List<char>();
bool negative = false;
if (a < 0)
{
negative = true;
a *= -1;
}
while (a > 0)
{
l.Add('0' + (char)(a % 10));
a /= 10;
}
if (negative) l.Add('-');
l.Reverse();
return new String(l.ToArray());
Solution 4 - C#
The integer is processed from the least significant digit to the most significant. A single digit is computed used modulo 10 (%10) which is then added to the character value of '0'. This results in one of the characters '0', '1', ... , '9'.
The digits are pushed onto a stack because they have to be presented in the reverse order as they are processed (most significant digit to least significant digit). Doing it like this instead of repeatedly prepending the digits to a string could be more efficient but because the number of digits is quite low you would have to perform a benchmark to be sure.
Some extra processing is required to handle non-positive numbers.
public string IntToString(int a) {
if (a == 0)
return "0";
if (a == int.MinValue)
return "-2147483648";
var isNegative = false;
if (a < 0) {
a = -a;
isNegative = true;
}
var stack = new Stack<char>();
while (a != 0) {
var c = a%10 + '0';
stack.Push((char) c);
a /= 10;
}
if (isNegative)
stack.Push('-');
return new string(stack.ToArray());
}
My first version used a StringBuilder
to create the string from the array of characters but getting the string "out of the" StringBuilder
requires a call to method named ToString
. Obviously, this method does not do any int to string conversion which to me is what this question is about.
But to prove that you can create a string without calling ToString
I have switched to using a string
constructor which I also would assume to be more efficient compared to using a StringBuilder
.
And if ToString
in any form is prohibited you cannot use string concatenation as seen in the documentation for string.Concat
:
> The method concatenates arg0 and arg1by calling the parameterless ToString method of arg0 and arg1; it does not add any delimiters.
so executing s += '1'
will call '1'.ToString()
. But to me this isn't important. The important part is how you convert an int to a string.
Solution 5 - C#
you can convert any digit to a char like this
byte = (char)(byte)(digit+48)
The magic number 48
is the ASCII value of the char 0
and they are sequential in the ASCII table thus you can just add the digit to get the corresponding value in the ASCII table.
and you can get the digits in the integer iteratively using the modulus operator %
Borrowing the general structure from pswg you would get
public string IntToString(int a) {
var str = string.Empty;
bool isNegative = false;
if (a < 0) {
isNegative = true;
a = -a;
}
do {
str = (char)(byte)((a % 10) + 48) + str;
a /= 10;
} while(a > 0);
return isNegative ? '-' + str : str
}
Solution 6 - C#
Aiming for a shorter version, and one that uses Math.DivRem
:
string IntToString(int a)
{
if (a == int.MinValue)
return "-2147483648";
if (a < 0)
return "-" + IntToString(-a);
if (a == 0)
return "0";
var s = "";
do
{
int r;
a = Math.DivRem(a, 10, out r);
s = new string((char)(r + (int)'0'), 1) + s;
}
while (a > 0);
return s;
}
The use of the new string(..., 1)
constructor is just a way to satisfy the OP's requirement that ToString
not be called on anything.
Solution 7 - C#
Here are my takes on it using iteration and recursion with run time analysis.
public static class IntegerToString
{
static char[] d = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
public static string Iteration(int num, int radix = 10)
{
if (num == 0) return "0";
if (num < 0) return "-" + Iteration(Math.Abs(num));
var r = new List<char>();
while (num > 0)
{
r.Insert(0, d[num % radix]);
num /= radix;
}
return new string(r.ToArray());
}
public static string Recursion(int num, int radix = 10)
{
if (num == 0) return "0";
if (num < 0) return "-" + Recursion(Math.Abs(num));
return (num > radix - 1 ? Recursion(num / radix) : "") + d[num % radix];
}
}
Key Points
- Handles base 2 up to 36 (CAVEAT: you'll have to ensure your base is correct since there is no exception handling.
- Recursion method is only 3 lines long! (code golf style)
Analysis
The following is the runtime analysis of both methods compared to the standard ToString()
on my computer.
50 runs of 100000 items per set
Running Time:
Iteration: 00:00:02.3459591 (00:00:00.0469191 avg)
Recursion: 00:00:02.1359731 (00:00:00.0427194 avg)
Standard : 00:00:00.4271253 (00:00:00.0085425 avg)
Ratios:
| Iter | Rec | Std
-----+------+------+-----
Iter | 1.00 | 0.91 | 0.18
Rec | 1.10 | 1.00 | 0.20
Std | 5.49 | 5.00 | 1.00
The results indicate the iteration and recursion methods run 5.49 and 5.00 times slower than the standard ToString()
method.
And here is the code I used for the analysis:
class Program
{
static void Main(string[] args)
{
var r = new Random();
var sw = new System.Diagnostics.Stopwatch();
var loop = new List<long>();
var recr = new List<long>();
var std = new List<long>();
var setSize = 100000;
var runs = 50;
Console.WriteLine("{0} runs of {1} items per set", runs, setSize);
for (int j = 0; j < runs; j++)
{
// create number set
var numbers = Enumerable.Range(1, setSize)
.Select(s => r.Next(int.MinValue,
int.MaxValue))
.ToArray();
// loop
sw.Start();
for (int i = 0; i < setSize; i++)
IntegerToString.Iteration(numbers[i]);
sw.Stop();
loop.Add(sw.ElapsedTicks);
// recursion
sw.Reset();
sw.Start();
for (int i = 0; i < setSize; i++)
IntegerToString.Recursion(numbers[i]);
sw.Stop();
recr.Add(sw.ElapsedTicks);
// standard
sw.Reset();
sw.Start();
for (int i = 0; i < setSize; i++)
numbers[i].ToString();
sw.Stop();
std.Add(sw.ElapsedTicks);
}
Console.WriteLine();
Console.WriteLine("Running Time:");
Console.WriteLine("Iteration: {0} ({1} avg)",
TimeSpan.FromTicks(loop.Sum()),
TimeSpan.FromTicks((int)loop.Average()));
Console.WriteLine("Recursion: {0} ({1} avg)",
TimeSpan.FromTicks(recr.Sum()),
TimeSpan.FromTicks((int)recr.Average()));
Console.WriteLine("Standard : {0} ({1} avg)",
TimeSpan.FromTicks(std.Sum()),
TimeSpan.FromTicks((int)std.Average()));
double lSum = loop.Sum();
double rSum = recr.Sum();
double sSum = std.Sum();
Console.WriteLine();
Console.WriteLine("Ratios: \n" +
" | Iter | Rec | Std \n" +
"-----+------+------+-----");
foreach (var div in new[] { new {n = "Iter", t = lSum},
new {n = "Rec ", t = rSum},
new {n = "Std ", t = sSum}})
Console.WriteLine("{0} | {1:0.00} | {2:0.00} | {3:0.00}",
div.n, lSum / div.t, rSum / div.t, sSum / div.t);
Console.ReadLine();
}
Solution 8 - C#
public static string integerToString(int integerPassedIn)
{
if (integerPassedIn == 0) return "0";
var negative = integerPassedIn < 0;
var res = new List<char>();
while(integerPassedIn != 0)
{
res.Add((char)(48 + Math.Abs(integerPassedIn % 10)));
integerPassedIn /= 10;
}
res.Reverse();
if (negative) res.Insert(0, '-');
return new string(res.ToArray());
}
Solution 9 - C#
Recursion:
public static string integerToString(int integerPassedIn)
{
ICollection<char> res = new List<char>();
IntToStringRecusion(integerPassedIn, res);
if (integerPassedIn < 0) res.Add('-');
return new string(res.Reverse().ToArray()).PadLeft(1,'0');
}
static void IntToStringRecusion(int integerPassedIn, ICollection<char> array)
{
if (integerPassedIn == 0) return;
array.Add((char)(48 + Math.Abs(integerPassedIn % 10)));
IntToStringRecusion(integerPassedIn / 10, array);
}
Solution 10 - C#
easy peasy:
string s = 5 + ""
//s = "5"