- ВКонтакте
- РћРТвЂВВВВВВВВнокласснРСвЂВВВВВВВВРєРСвЂВВВВВВВВ
- РњРѕР№ Р В Р’В Р РЋРЎв„ўР В Р’В Р РЋРІР‚ВВВВВВВВРЎР‚
- Viber
- Skype
- Telegram
C# Некорректно работает вывод символов из переменной string
c# почему скрипт ниже при вводе 10000 на выход выдает 97,0К? а при 100000 145К?
private string Output(int value)
{
string text, lasttext = "";
text = value.ToString();
switch (text.Length)
{
case int n when n <= 3:
lasttext = "";
return text;
case int n when n > 3 && n <= 6:
lasttext = "K";
break;
case int n when n > 6 && n <= 9:
lasttext = "M";
break;
case int n when n > 9 && n <= 12:
lasttext = "B";
break;
case int n when n > 12 && n <= 15:
lasttext = "T";
break;
case int n when n > 15 && n <= 18:
lasttext = "Q";
break;
}
switch (text.Length % 3)
{
case 1:
text = text[0] + "," + text[1] + text[2] + lasttext;
break;
case 2:
text = text[0] + text[1] + "," + text[2] + lasttext;
break;
case 0:
text = text[0] + text[1] + text[2] + lasttext;
break;
}
return text;
}
Ответы (3 шт):
Куда проще записывать эти ультра большие числа для Idle игр, структурой.
[Serializable]
public struct BigNumber
{
public float Number;
public int ZeroCount;
...
}
Если Number
переваливает за 100, число делится на 10, а ZeroCount
увеличивается и на оборот.
...
public BigNumber (float number, int zeroCount)
{
Number = number;
ZeroCount = zeroCount;
Optimize();
}
private void Optimize ()
{
if (Number > 1000)
{
Number /= 10f;
ZeroCount++;
Optimize();
}
else if (Number < 100 && zeroCount > 0)
{
Number *= 10f;
ZeroCount--;
Optimize();
}
}
И самое чудесное, это то что можно прописать математические операции +
и -
для этой структуры.
...
public static BigNumber operator + (BigNumber a, BigNumber b)
{
if (a.ZeroCount == b.ZeroCount)
{
return new BigNumber(a.Number + b.Number, a.ZeroCount);
}
else if (a.ZeroCount > b.ZeroCount)
{
int zeroDelta = a.ZeroCount - b.ZeroCount;
if (zeroDelta > 6)
return a; // b much less
int ANumber = a.ZeroCount * 10f * zeroDelta;
return new BigNumber(ANumber * b.Number, b.ZeroCount);
}
else
{
return b + a;
}
}
public static BigNumber operator - (BigNumber a, BigNumber b)
{
...
}
Что бы получить суффикс достаточно ZeroCount / 3
и получишь индекс в массиве суфиксов, не нужен никакой стоэтажный switch
. И выполнить это можно для удобства в перегрузке ToString()
.
private const string[] Suffix = new[] {"", "k", "m", "t" ...};
...
public override string ToString ()
{
int dIndex = ZeroCount / 3;
string suffix = Suffix[dIndex];
return Number.ToString("F2") + suffix;
}
Если цифры совсем запредельные, ZeroCount
можно поменять на long
. Если нужна большая точьность для прибавления малых числел, Number
можно поменять на double
.
Давайте посмотрим, что тут происходит. В вашем первом случае исполняется эта ветка кода:
case 2:
text = text[0] + text[1] + "," + text[2] + lasttext;
^^^^^^^^^^^^^^^^^
// 97,0K
Посмотрим первые два слагаемых:
text[0]
- это типchar
, значение'1'
, код символа49
text[1]
- это типchar
, значение'0'
, код символа48
Когда эти два значения складываются, они складываются именно как числа, равные коду символов и получается... число 97
! Дальше следует прибавление строки. Вот в этот момент 97
преобразуется в строку "97"
и дальше уже идёт обычная конкатенация строк, что бы там справа не прибавлялось.
"Лечится" такое элементарно - просто начинаем формирование переменной с пустой строки и всё тогда сразу преобразуется в строки и конкатенируется обычным образом:
text = "" + text[0] + text[1] + "," + text[2] + lasttext;
^^^^^
// 10,0K
Но ещё лучше использовать современную интерполяцию строк, тогда уже точно никаких неожиданностей не будет, всё будет подставляться и форматироваться строго как укажете:
text = $"{text[0]}{text[1]},{text[2]}{lasttext}";
// 10,0K
Почему вы получаете такой результат, вам ответ уже дали. Тут я лишь чуть подробней распишу то, о чем написал в комментарии.
Вашу задачу можно (и мне кажется нужно) решать математическим путем, без преобразований числа в строку, без кучи 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