Почему вы получаете такой результат, вам ответ уже дали. Тут я лишь чуть подробней распишу то, о чем написал в комментарии.
Вашу задачу можно (и мне кажется нужно) решать математическим путем, без преобразований числа в строку, без кучи if
и switch
, всего несколько строк кода с довольно простыми расчетами.
Смотрите, вот есть число 1 234 567
, как видите, числа идут некими группами по 3 числа, по которым можно понять, что если группа одна, то это "тысячи", если две, то уже миллионы, три миллиарды, и так далее. Для подсчета этого добра математически, очень классно могут помочь логарифмы, а точнее log10
. Если мы воспользуемся им, то увидим такую картину: log10(1) = 0
, log10(12) = 1
, log10(123) = 2
... Как видите, это дает нам кол-во чисел после первого, что нам и надо. Теперь делим это число на 3 (log10(1000) = 3
) и получаем кол-во групп (log10(1 000)/3 = 1
, log10(1 000 000)/3 = 2
, log10(1 000 000 000)/3 = 3
). Если быть более точным, то так мы нашли с вами порядок числа.
Теперь давайте число сделаем более компактным (масштрабируем число), то есть, если число 1 234 567
, то мы знаем, что это 1 миллион с копейками, а если точнее, то 1,2
. Вот чтобы число так сократить, нам надо поделить его на 1000 в степени, которую получили ранее, в итоге получаем такое: 1 234 567/1000^2 = 1,234567
Вот и вся математика, как видите, все довольно просто. Теперь напишем это на C#.
Вся математика находится в классе Math
, из которого нам нужен метод Log10
и метод возведения в степень (Pow
). Получаем тогда такой код:
var number = 1_234_567;
var exponent = (int)Math.Log10(number) / 3; //2
var scaled = number / Math.Pow(1000, exponent); // 1.234567
Основное у нас есть, осталось сформировать нужный вид, а точнее "обрезать" число и добавить суффикс (букву в конец).
Суффиксы - с ними все до безобразия просто. Делаем массив из всех нужных обозначений, допустим var suffixes = new[] { "K", "M", "B", "T", "Qa", "Qi" };
, а дальше берем по индексу нужное, например так: suffixes[exponent - 1]
(индекс с 0 идет).
Формат - Можете делать как вы, "склеивая" строки, ну а я сделаю что-то такое $"{scaled:0.#}{suffix}"
. Тут используется интерполяция строк с форматированием чисел. Результатом будет 1,2M
Весь конечный код будет таким:
var number = 1_234_567;
var exponent = (int)Math.Log10(number) / 3;
var scaled = number / Math.Pow(1000, exponent);
var suffixes = new[] { "K", "M", "B", "T", "Qa", "Qi" };
var suffix = suffixes[exponent - 1];
var result = $"{scaled:0.#}{suffix}";
Тут нету ряда проверок, из-за чего он будет падать, если число будет выше Qi
, ну или ниже K
(например 1
), моей задачей было лишь показать принцип, а дальше уже крутите как вам нужно.
Теперь давайте поговорим про удобство. Я сейчас абстрагируюсь от Unity, ибо буду использовать современные аспекты языка, которых в Unity нету, но в конце я скажу как можно это обойти.
Обычно для решения таких задач пишут методы расширения, которые расширяют определенный тип, и которые мы можем вызвать в любом месте. Собственно, давайте сделаем такой метод, да еще и сделаем его универсальным, чтобы он подходил к любому числовому типу.
public static class FormatExtensions
{
public static string ToHuman<T>(this T number) where T : INumber<T>
{
var doubleValue = double.CreateChecked(number);
var suffixes = new[] { "K", "M", "B", "T", "Qa", "Qi" };
var exponent = doubleValue < 1000 ? 0 : (int)(Math.Log10(doubleValue) / 3);
var divisor = Math.Pow(1000, exponent);
return exponent == 0
? doubleValue.ToString("0.#") // Число меньше 1000
: exponent - 1 < suffixes.Length
? $"{doubleValue / divisor:0.#}{suffixes[exponent - 1]}"
: $"{doubleValue / Math.Pow(1000, suffixes.Length):0.#}{suffixes[^1]}"; // Вышли за пределы массива суффиксов
}
}
T
- Это Generic тип, который позволяет не привязываться к чему-то одному.
this T
- через this
мы получаем конкретное значение, к которому будет применен этот метод.
where T : INumber<T>
- в .NET 7 появился данный интерфейс, который реализуют все стандартные числовые типы. В более ранних версиях тут можно попробовать задать IComparable
.
CreateChecked()
- Тоже нововведение .NET 7. Метод создает конкретный тип (в нашем случае double
, чтобы не терять дробную часть, если есть) из INumber<>
. В более ранних версиях можно попробовать воспользоваться Convert.ToDouble()
.
doubleValue < 1000 ? 0
- Тут проверка, если число меньше 1000, то отдаем 0, по которому дальше мы понимаем, что суффиксы и прочие приблуды добавлять нет смысла.
exponent - 1 < suffixes.Length
- Не даем выйти за пределы длины массива суффиксов.
$"{doubleValue / Math.Pow(1000, suffixes.Length):0.#}{suffixes[^1]}";
- Если подходящего суффикса нет, то масштабируем число под последний доступный и выводим его. (Как по мне, тут лучше уже научную нотацию использовать ($"{doubleValue:e2}"; например
), тогда будет и компактней и понятней).
Использование будет таким:
var result = 1_234_567.ToHuman(); // "1,2M"
Простые тесты:
1.ToHuman(); // 1
1_000.ToHuman(); // 1K
1_000_000.ToHuman(); // 1M
1_000_000_000.ToHuman(); // 1B
1.281.ToHuman(); // 1,3
572_353.286.ToHuman(); // 572,4K
1_751.92f.ToHuman(); // 1,8K
74_626_224L.ToHuman(); // 74,6M
int.MaxValue.ToHuman(); // 2,1B
double.MaxValue.ToHuman(); // 1797693134862320000000000000000000000000000000000000..(куча нулей)..Qi ... (нет подходящего суффикса в массиве)
float.MaxValue.ToHuman(); // 340282346638529000000Qi (нет подходящего суффикса в массиве)
long.MaxValue.ToHuman(); // 9,2Qi