diff --git a/XStringUtils.slnx b/XStringUtils.slnx
new file mode 100644
index 0000000..6f40209
--- /dev/null
+++ b/XStringUtils.slnx
@@ -0,0 +1,3 @@
+
+
+
diff --git a/XStringUtils/Prettify.cs b/XStringUtils/Prettify.cs
new file mode 100644
index 0000000..88af9a6
--- /dev/null
+++ b/XStringUtils/Prettify.cs
@@ -0,0 +1,113 @@
+using System.Globalization;
+using System.Numerics;
+
+namespace XStringUtils;
+
+public static partial class XStringUtils
+{
+ public static string PrettifyTiny(double number)
+ {
+ return number > 0.0001
+ ? number.ToString("F4", CultureInfo.InvariantCulture)
+ : number.ToString("E3", CultureInfo.InvariantCulture);
+ }
+
+ public enum Notation
+ {
+ KMB,
+ Logarithmic10,
+ LogarithmicE,
+ }
+
+ extension(T number) where T : IFloatingPoint, ILogarithmicFunctions, IPowerFunctions
+ {
+ public string Prettify(Notation notation = Notation.KMB)
+ {
+ if (!T.IsFinite(number))
+ {
+ return "∞";
+ }
+
+ if (number == T.Zero)
+ {
+ return PrettifySub(0.0);
+ }
+
+ if (number < T.Zero)
+ {
+ return "-" + Prettify(-number, notation);
+ }
+
+ if (number < T.CreateChecked(0.005))
+ {
+ return number.ToString("E2", CultureInfo.InvariantCulture);
+ }
+
+ int @base = int.CreateChecked(T.Floor(T.Log(number) / T.Log(T.CreateChecked(1000))));
+
+ if (@base <= 0)
+ {
+ return PrettifySub(number);
+ }
+
+ if (notation == Notation.Logarithmic10 || notation == Notation.LogarithmicE)
+ {
+ T logBase = notation == Notation.Logarithmic10 ? T.CreateChecked(10) : T.E;
+ T exponent = T.Log(number, logBase);
+ return PrettifySub(exponent) + "L" + logBase;
+ }
+
+ T thousand = T.CreateChecked(1000);
+ T tbase = T.CreateChecked(@base);
+
+ number /= T.Pow(thousand, tbase);
+
+ // Prevent 1000K, 1000M, etc.
+ if (number >= T.CreateChecked(999.5))
+ {
+ number /= thousand;
+ @base++;
+ }
+
+ string suffix = ResolveSuffix(@base);
+
+ return PrettifySub(number) + suffix;
+ }
+ }
+
+ private static string PrettifySub(T number) where T : IFloatingPoint
+ {
+ T floor = T.Floor(number);
+
+ if (number == floor)
+ {
+ return number.ToString(null, CultureInfo.InvariantCulture);
+ }
+
+ int precision = 3 - floor.ToString(null, CultureInfo.InvariantCulture).Length;
+
+ return number.ToString("F" + precision, CultureInfo.InvariantCulture);
+ }
+
+ private static readonly string[] Suffices =
+ [
+ "K","M","B","T","Qa","Qi","Sx","Sp","Oc","No","Dc","Ud",
+ "Dd","Td","Qad","Qid","Sxd","Spd","Od","Nd","V","Uv","Dv",
+ "Tv","Qav","Qiv","Sxv","Spv","Ov","Nv","Tg","Utg","Dtg","Ttg",
+ "Qatg","Qitg","Sxtg","Sptg","Otg","Ntg","Qaa","Uqa","Dqa","Tqa",
+ "Qaqa","Qiqa","Sxqa","Spqa","Oqa","Nqa","Qia","Uqi","Dqi",
+ "Tqi","Qaqi","Qiqi","Sxqi","Spqi","Oqi","Nqi","Sxa","Usx",
+ "Dsx","Tsx","Qasx","Qisx","Sxsx","Spsx","Osx","Nsx","Spa",
+ "Usp","Dsp","Tsp","Qasp","Qisp","Sxsp","Spsp","Osp","Nsp",
+ "Og","Uog","Dog","Tog","Qaog","Qiog","Sxog","Spog","Oog",
+ "Nog","Na","Un","Dn","Tn","Qan","Qin","Sxn","Spn","On",
+ "Nn","Ct","Uc"
+ ];
+
+ private static string ResolveSuffix(int @base)
+ {
+ return @base <= Suffices.Length
+ ? Suffices[@base - 1]
+ : "e" + (@base * 3);
+ }
+}
diff --git a/XStringUtils/XStringUtils.csproj b/XStringUtils/XStringUtils.csproj
new file mode 100644
index 0000000..ce8a890
--- /dev/null
+++ b/XStringUtils/XStringUtils.csproj
@@ -0,0 +1,34 @@
+
+
+
+ net10.0
+ enable
+ enable
+ False
+ True
+ XStringUtils
+ SacredFloof
+ SacredFloof
+ Small string utilities. AOT-friendly
+ SacredFloof 2026
+ Utility
+ 1.0.1
+
+
+
+ True
+ True
+ True
+
+
+
+ True
+ True
+ True
+
+
+
+
+
+
+