我正在尝试创建一个无尽的游戏,如Tap Titans,Clicker Heroes等。我有一个BigInteger类,只要它们适合内存就能够表示任意大整数。
现在我有一个将BigInteger格式化为特定格式的类。它使用K(千),M(百万),B(十亿),T(万亿),Q(千万亿)用于“小”和“小”。数字,但在此之后,简写符号变得模棱两可,不直观。由于Quintillion,Q已经模糊不清了,但我可以接受。
Q之后,我想从字母a开始。因此1000Q = 1.000a,然后1000a = 1.000b等。当达到1000z时,应将其格式化为1.000aa。然后1000aa = 1.000 ab,1000 az = 1.000 ba,1000 bz = 1.000 ca等。
到目前为止,我已经实现了上述目标,但是我的班级无法在1000zz之后格式化数字。我无法想出一个自动确定需要多少个字符的通用算法(对于极大数字可能是aaaz)。
我的课程如下:
public class NumericalFormatter : BigIntegerFormatter
{
public string Format(BigInteger number)
{
return FormatNumberString(number.ToString());
}
private string FormatNumberString(string number)
{
if (number.Length < 5)
{
return number;
}
if (number.Length < 7)
{
return FormatThousands(number);
}
return FormatGeneral(number);
}
private string FormatThousands(string number)
{
string leadingNumbers = number.Substring(0, number.Length - 3);
string decimals = number.Substring(number.Length - 3);
return CreateNumericalFormat(leadingNumbers, decimals, "K");
}
private string CreateNumericalFormat(string leadingNumbers, string decimals, string suffix)
{
return String.Format("{0}.{1}{2}", leadingNumbers, decimals, suffix);
}
private string FormatGeneral(string number)
{
int amountOfLeadingNumbers = (number.Length - 7) % 3 + 1;
string leadingNumbers = number.Substring(0, amountOfLeadingNumbers);
string decimals = number.Substring(amountOfLeadingNumbers, 3);
return CreateNumericalFormat(leadingNumbers, decimals, GetSuffixForNumber(number));
}
private string GetSuffixForNumber(string number)
{
int numberOfThousands = (number.Length - 1) / 3;
switch (numberOfThousands)
{
case 1:
return "K";
case 2:
return "M";
case 3:
return "B";
case 4:
return "T";
case 5:
return "Q";
default:
return GetProceduralSuffix(numberOfThousands - 5);
}
}
private string GetProceduralSuffix(int numberOfThousandsAfterQ)
{
if (numberOfThousandsAfterQ < 27)
{
return ((char)(numberOfThousandsAfterQ + 96)).ToString();
}
int rightChar = (numberOfThousandsAfterQ % 26);
string right = rightChar == 0 ? "z" : ((char)(rightChar + 96)).ToString();
string left = ((char)(((numberOfThousandsAfterQ - 1) / 26) + 96)).ToString();
return left + right;
}
}
正如您所看到的,getProceduralSuffix()
方法无法处理会导致两个以上字符后缀的BigIntegers。
我还有一个单元测试来验证这个类的功能(准备一些侧滚动):
namespace UnitTestProject.BigIntegerTest
{
[TestClass]
public class NumericalformatterTest
{
[TestMethod]
public void TestFormatReturnsNumericalFormat()
{
BigIntegerFormatter numericalFormatter = new NumericalFormatter();
foreach (string[] data in DpNumbersAndNumericalFormat())
{
BigInteger number = new BigInteger(data[0]);
string expectedNumericalFormat = data[1];
Assert.AreEqual(expectedNumericalFormat, numericalFormatter.Format(number));
}
}
private string[][] DpNumbersAndNumericalFormat()
{
return new string[][]
{
new string[] { "0", "0" },
new string[] { "1", "1" },
new string[] { "15", "15" },
new string[] { "123", "123" },
new string[] { "999", "999" },
new string[] { "1000", "1000" },
new string[] { "9999", "9999" },
new string[] { "10000", "10.000K" },
new string[] { "78456", "78.456K" },
new string[] { "134777", "134.777K" },
new string[] { "999999", "999.999K" },
new string[] { "1000000", "1.000M" },
new string[] { "12345000", "12.345M" },
new string[] { "999999000", "999.999M" },
new string[] { "1000000000", "1.000B" },
new string[] { "12345678900", "12.345B" },
new string[] { "123345678900", "123.345B" },
new string[] { "1233000000000", "1.233T" },
new string[] { "9999000000000", "9.999T" },
new string[] { "12233000000000", "12.233T" },
new string[] { "99999000000000", "99.999T" },
new string[] { "100000000000000", "100.000T" },
new string[] { "456789000000000", "456.789T" },
new string[] { "999999000000000", "999.999T" },
new string[] { "1000000000000000", "1.000Q" },
new string[] { "10000000000000000", "10.000Q" },
new string[] { "100000000000000000", "100.000Q" },
new string[] { "999999000000000000", "999.999Q" },
new string[] { "1000000000000000000", "1.000a" },
new string[] { "10000000000000000000", "10.000a" },
new string[] { "100000000000000000000", "100.000a" },
new string[] { "1000000000000000000000", "1.000b" },
new string[] { "1000000000000000000000000", "1.000c" },
new string[] { "1000000000000000000000000000", "1.000d" },
new string[] { "1000000000000000000000000000000", "1.000e" },
new string[] { "1000000000000000000000000000000000", "1.000f" },
new string[] { "1000000000000000000000000000000000000", "1.000g" },
new string[] { "1000000000000000000000000000000000000000", "1.000h" },
new string[] { "1000000000000000000000000000000000000000000", "1.000i" },
new string[] { "1000000000000000000000000000000000000000000000", "1.000j" },
new string[] { "1000000000000000000000000000000000000000000000000", "1.000k" },
new string[] { "1000000000000000000000000000000000000000000000000000", "1.000l" },
new string[] { "1000000000000000000000000000000000000000000000000000000", "1.000m" },
new string[] { "1000000000000000000000000000000000000000000000000000000000", "1.000n" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000", "1.000o" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000", "1.000p" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000", "1.000q" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000", "1.000r" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000", "1.000s" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000t" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000u" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000v" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000w" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000x" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000y" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000z" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000aa" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ab" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ac" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ad" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ae" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000af" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ag" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ah" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ai" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000aj" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ak" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000al" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000am" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000an" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ao" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ap" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000aq" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ar" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000as" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000at" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000au" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000av" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000aw" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ax" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ay" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000az" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ba" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bb" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bc" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bd" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000be" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bf" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bg" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bh" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bi" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bj" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bt" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000by" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bz" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ca" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000cb" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000cc" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000cd" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ce" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ct" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000cy" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000cz" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000da" },
new string[] { "1234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.234da" },
new string[] { "123456000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "123.456da" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000db" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000dr" },
new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000dz" },
};
}
}
}
此时所有的测试都通过了。缺少的测试是检查1000 ^ (26 ^ 3 + 5)
(+5是因为a
格式化开始后Q
)可以格式化为&#34; 1.000aaa&#34;的测试。
如何按照上面描述的方式编写程序大整数的格式。
答案 0 :(得分:1)
在数字符号的世界中,这实际上是一个已解决的问题。也就是说,您可以使用scientific notation来表示这些特别大的数字。科学记数法是紧凑的,允许尾数的任意精度,并且易于理解。就个人而言,这就是我采取的方法。
为了便于讨论,让我们看看你还有其他选择......
从表面上看,您的请求可归结为文本转换的直接数字基值。正如我们可以将数值转换为其文本表示形式,例如base 2,base 10,base 16等。我们可以使用base 26将数值转换为文本表示,只使用字母{{1} }通过a
作为数字。
然后你的z
方法看起来像这样:
GetProceduralSuffix()
static string GetProceduralSuffix(int value)
{
StringBuilder sb = new StringBuilder();
while (value > 0)
{
int digit = value % 26;
sb.Append((char)('a'+ digit));
value /= 26;
}
if (sb.Length == 0)
{
sb.Append('a');
}
sb.Reverse();
return sb.ToString();
}
扩展方法是这样的:
Reverse()
然而,上述问题存在轻微问题。在以这种方式表示的基数26中,数字public static void Reverse(this StringBuilder sb)
{
for (int i = 0, j = sb.Length - 1; i < sb.Length / 2; i++, j--)
{
char chT = sb[i];
sb[i] = sb[j];
sb[j] = chT;
}
}
对应于a
,因此您的后缀将永远不会以字母0
开头,至少不会在第一个字母a
之后(&#39;特殊情况,就像使用十进制表示法时一样,我们使用数字0
来表示零值)。相反,您可以在ba
后z
和baa
之后zz
获得aaaz
。
就个人而言,我认为这很好。它会排除像a
这样的后缀,但仅仅因为后缀符号系统是合乎逻辑的,可预测的,并且容易被逆转(即给出后缀,它可以很容易地弄清楚数字意味着什么)。
但是,如果您坚持使用z
... aa
,zz
... aaa
,zzz
... aaaa
,{{ 1}} ...依此类推,您可以使用基数27而不是26,a
... z
以外的字符作为0
数字,并预先计算跳过后缀的值你去了一个0位数,然后索引结果。例如:
List<string> _hackedValues = new List<string>();
static void PrecomputeValues()
{
// 531441 = 27 ^ 4, i.e. the first 5-digit base 27 number.
// That's a large enough number to ensure that the output
// include "aaaz", and indeed almost all of the 4-digit
// base 27 numbers
for (int i = 0; i < 531441; i++)
{
string text = ToBase27AlphaString(i);
if (!text.Contains('`'))
{
_hackedValues.Add(text);
}
}
}
static string GetProceduralSuffix(int value)
{
if (hackedValues.Count == 0)
{
PrecomputeValues();
}
return _hackedValues[value];
}
static string ToBase27AlphaString(int value)
{
StringBuilder sb = new StringBuilder();
while (value > 0)
{
int digit = value % 27;
sb.Append((char)('`'+ digit));
value /= 27;
}
if (sb.Length == 0)
{
sb.Append('`');
}
sb.Reverse();
return sb.ToString();
}
这是一个完整的程序,用于说明这两种技术,显示&#34;有趣的&#34;输入(即输出中字符数改变的地方):
class Program
{
static void Main(string[] args)
{
int[] values = { 0, 25, 26, 675, 676 };
foreach (int value in values)
{
Console.WriteLine("{0}: {1}", value, ToBase26AlphaString(value));
}
Console.WriteLine();
List<Tuple<int, string>> hackedValues = new List<Tuple<int, string>>();
for (int i = 0; i < 531441; i++)
{
string text = ToBase27AlphaString(i);
if (!text.Contains('`'))
{
hackedValues.Add(Tuple.Create(i, text));
}
}
Tuple<int, string> prev = null;
for (int i = 0; i < hackedValues.Count; i++)
{
Tuple<int, string> current = hackedValues[i];
if (prev == null || prev.Item2.Length != current.Item2.Length)
{
if (prev != null)
{
DumpHackedValue(prev, i - 1);
}
DumpHackedValue(current, i);
}
prev = current;
}
}
private static void DumpHackedValue(Tuple<int, string> hackedValue, int i)
{
Console.WriteLine("{0}: {1} (actual value: {2})", i, hackedValue.Item2, hackedValue.Item1);
}
static string ToBase26AlphaString(int value)
{
return ToBaseNAlphaString(value, 'a', 26);
}
static string ToBase27AlphaString(int value)
{
return ToBaseNAlphaString(value, '`', 27);
}
static string ToBaseNAlphaString(int value, char baseChar, int numericBase)
{
StringBuilder sb = new StringBuilder();
while (value > 0)
{
int digit = value % numericBase;
sb.Append((char)(baseChar + digit));
value /= numericBase;
}
if (sb.Length == 0)
{
sb.Append(baseChar);
}
sb.Reverse();
return sb.ToString();
}
}
static class Extensions
{
public static void Reverse(this StringBuilder sb)
{
for (int i = 0, j = sb.Length - 1; i < sb.Length / 2; i++, j--)
{
char chT = sb[i];
sb[i] = sb[j];
sb[j] = chT;
}
}
}
答案 1 :(得分:1)
经过长时间的思考(实际上避免了一个月的工作)和几个小时的编码,我用了一部分代码来创建自己的解决方案。
按顺序使用前缀:空字符串,k,M,B,Q,a,b ... z (不包括k,因为成千上万),aa,bb,..., zz,aaa,bbb,...,zzz等。它从数字的末尾修剪零,例如1000 = 1k。
(也有可能使用科学计数法,但它不会修剪零。)
using System.Collections.Generic;
using System.Numerics;
/// <summary>
/// Static class used to format the BigIntegers.
/// </summary>
public static class BigIntegerFormatter
{
private static List<string> suffixes = new List<string>();
/// <summary>
/// If it's equal to 0, there are only suffixes from an empty string to Q on the suffixes list.
/// If it's equal to 1, there are a - z suffixes added.
/// If it's equal to 2, there are aa - zz suffixes added and so on.
/// </summary>
private static int suffixesCounterForGeneration = 0;
/// <summary>
/// Formats BigInteger using scientific notation. Returns a number without the exponent if the length
/// of the number is smaller than 4.
/// </summary>
/// <param name="number">Number to format.</param>
/// <returns>Returns string that contains BigInteger formatted using scientific notation.</returns>
public static string FormatScientific(BigInteger number)
{
return FormatNumberScientificString(number.ToString());
}
/// <summary>
/// Formats BigInteger using engineering notation - with a suffix. Returns a number without the
/// suffix if the length of the number is smaller than 4.
/// </summary>
/// <param name="number">Number to format.</param>
/// <returns>Returns string that contains BigInteger formatted using engineering notation.</returns>
public static string FormatWithSuffix(BigInteger number)
{
return FormatNumberWithSuffixString(number.ToString());
}
private static string FormatNumberScientificString(string numberString)
{
// if number length is smaller than 4, just returns the number
if (numberString.Length < 4) return numberString;
// Exponent counter. E.g. for 1000 it will be 3 and the number will
// be presented as 1.000e3 because 1000.Length = 4
var exponent = numberString.Length - 1;
// Digit before a comma. Always only one.
var leadingDigit = numberString.Substring(0, 1);
// Digits after a comma. Always three of them.
var decimals = numberString.Substring(1, 3);
// Returns the number in scientific format.
// Example: 12345 -> 1.234e4
return $"{leadingDigit}.{decimals}e{exponent}";
}
private static string FormatNumberWithSuffixString(string numberAsString)
{
// if number length is smaller than 4, just returns the number
if (numberAsString.Length < 4) return numberAsString;
// Counts scientific exponent. This will be used to determine which suffix from the
// suffixes List should be used.
var exponentIndex = numberAsString.Length - 1;
// Digits before a comma. Can be one, two or three of them - that depends on the exponentsIndex.
var leadingDigit = "";
// Digits after a comma. Always three of them or less, if the formatted number will have zero
// on its end.
var decimals = "";
// Example: if the number the methods is formatting is 12345, exponentsIndex is 4, 4 % 3 = 1.
// There will be two leading digits. There will be three decimals. Formatted number will look like:
// 12.345k
switch (exponentIndex % 3)
{
case 0:
leadingDigit = numberAsString.Substring(0, 1);
decimals = numberAsString.Substring(1, 3);
break;
case 1:
leadingDigit = numberAsString.Substring(0, 2);
decimals = numberAsString.Substring(2, 3);
break;
case 2:
leadingDigit = numberAsString.Substring(0, 3);
decimals = numberAsString.Substring(3, 3);
break;
}
// Trims zeros from the number's end.
var numberWithoutSuffix = $"{leadingDigit}.{decimals}";
numberWithoutSuffix = numberWithoutSuffix.TrimEnd('0').TrimEnd('.');
var suffix = GetSuffixForNumber(exponentIndex / 3);
// Returns number in engineering format.
// return $"{numberWithoutSuffix}{suffixes[exponentIndex / 3]}";
return $"{numberWithoutSuffix}{suffix}";
}
/// <summary>
/// Gets suffix under a given index which is actually a number of thousands.
/// </summary>
/// <param name="suffixIndex">Suffix index. Number of thousands.</param>
/// <returns>Suffix under a given index - suffix for a given number of thousands.</returns>
private static string GetSuffixForNumber(int suffixIndex)
{
// Creates initial suffixes List with an empty string, k, M, B and Q
if (suffixes.Count == 0) suffixes = CreateSuffixesList();
// Fills the suffixes list if there's a need to
if (suffixes.Count - 1 < suffixIndex) FillSuffixesList(suffixes, suffixIndex);
return suffixes[suffixIndex];
}
private static List<string> CreateSuffixesList()
{
var suffixesList = new List<string>
{
"", "k", "M", "B", "Q"
};
return suffixesList;
}
private static void FillSuffixesList(List<string> suffixesList, int suffixIndex)
{
// while the suffixes list length - 1 is smaller than the suffix index of the suffix that we need
// (e.g.: when there's a need for an 'a' suffix:
// when suffixesList = "", "k", "M", "B", "Q"
// suffixesList.Count = 5, suffixIndex for a 'Q' is 4,
// suffixIndex for an 'a' is 5)
while (suffixesList.Count - 1 < suffixIndex)
{
// happens only once, when suffixList is filled only with
// initial values
if (suffixesCounterForGeneration == 0)
{
for (int i = 97; i <= 122; i++)
{
// k excluded because of thousands suffix
if (i == 107) continue;
// cache the character a - z
char character = (char)i;
suffixesList.Add(char.ToString(character));
}
suffixesCounterForGeneration++;
}
else
{
// for every character (a - z) counts how many times the character should be generated as the suffix
for (var i = 97; i <= 122; i++)
{
// cache the character a - z
char character = (char)i;
// placeholder for a generated suffix
string generatedSuffix = "";
// counts how many times one character should be used as one suffix and adds them
// basing on the suffixesCounterForGeneration which is the number telling us how many times
// the suffixes were generated
for (var counter = 1; counter <= suffixesCounterForGeneration + 1; counter++)
{
generatedSuffix += character.ToString();
}
// adds the generated suffix to the suffixes list
suffixesList.Add(generatedSuffix);
}
suffixesCounterForGeneration++;
}
}
}
}