Именование больших чисел

Лучший способ помочь другим, поделиться своими находками.

Именование больших чисел

Сообщение 1max1 29 июн 2020, 21:40

Недавно понадобилось сделать перевод больших чисел в более читабельный вид, по типу:
1 000 000 -> 1.00 Million
1 000 000 000 -> 1.00 Billion
...

На хабре есть хорошая статья https://habr.com/ru/post/314512/, но увы исходных код там не сохранился и пришлось накостылять самому :ymparty:

Советы по оптимизации приветствуются т.к. за 100к проходок в цикле оно жрет ~1000мс :-?

Использование: Tools.GetPrettyNumber(1e123) -> 1.00 Quadragintillion

Синтаксис:
Используется csharp
using System;
using System.Collections.Generic;

namespace MaxUtilities
{
    public static partial class Tools
    {
        class BigNumberPrefix
        {
            public enum Type
            {
                Units,
                Tens,
                Hundreds
            }

            public string prefix;
            public string modifier;
            public string correctTens;

            public BigNumberPrefix(string prefix, string modifier = null)
            {
                this.prefix = prefix;
                this.modifier = modifier;

                correctTens = prefix.Remove(prefix.Length - 1) + 'i';
            }
            public BigNumberPrefix GetWithModifier(BigNumberPrefix unitsPrefix)
            {
                if (!string.IsNullOrEmpty(modifier))
                {
                    switch (unitsPrefix.prefix)
                    {
                        case "tre":
                            if (modifier.Contains("S"))
                                return new BigNumberPrefix("tres");
                            break;

                        case "se":
                            if (modifier.Contains("S"))
                                return new BigNumberPrefix("ses");
                            if (modifier.Contains("X"))
                                return new BigNumberPrefix("sex");
                            break;

                        case "septe":
                            if (modifier.Contains("M"))
                                return new BigNumberPrefix("septem");
                            if (modifier.Contains("N"))
                                return new BigNumberPrefix("septen");
                            break;

                        case "nove":
                            if (modifier.Contains("M"))
                                return new BigNumberPrefix("novem");
                            if (modifier.Contains("N"))
                                return new BigNumberPrefix("noven");
                            break;
                    }
                }

                return unitsPrefix;
            }

            public static implicit operator bool(BigNumberPrefix prefix)
            {
                return prefix != null;
            }
            public static implicit operator string(BigNumberPrefix prefix)
            {
                return prefix.prefix;
            }
        }

        static readonly string[] simpleNames = new string[]
        {
            "Million",
            "Billion",
            "Trillion",
            "Quadrillion",
            "Quintillion",
            "Sextillion",
            "Septillion",
            "Octillion",
            "Nonillion"
        };

        static readonly Dictionary<BigNumberPrefix.Type, BigNumberPrefix[]> prefixes =
                    new Dictionary<BigNumberPrefix.Type, BigNumberPrefix[]>()
                    {
                        {
                            BigNumberPrefix.Type.Units, new BigNumberPrefix[]
                            {
                                new BigNumberPrefix("un"),
                                new BigNumberPrefix("duo"),
                                new BigNumberPrefix("tre"),
                                new BigNumberPrefix("quattuor"),
                                new BigNumberPrefix("quin"),
                                new BigNumberPrefix("se"),
                                new BigNumberPrefix("septe"),
                                new BigNumberPrefix("octo"),
                                new BigNumberPrefix("nove")
                            }
                        },

                        {
                            BigNumberPrefix.Type.Tens, new BigNumberPrefix[]
                            {
                                new BigNumberPrefix("deci",         "N"),
                                new BigNumberPrefix("viginti",      "MS"),
                                new BigNumberPrefix("triginta",     "NS"),
                                new BigNumberPrefix("quadraginta",  "NS"),
                                new BigNumberPrefix("quinquaginta", "NS"),
                                new BigNumberPrefix("sexaginta",    "N"),
                                new BigNumberPrefix("septuaginta",  "N"),
                                new BigNumberPrefix("octoginta",    "MX"),
                                new BigNumberPrefix("nonaginta")
                            }
                        },

                        {
                            BigNumberPrefix.Type.Hundreds, new BigNumberPrefix[]
                            {
                                new BigNumberPrefix("centi",        "NX"),
                                new BigNumberPrefix("ducenti",      "N"),
                                new BigNumberPrefix("trecenti",     "NS"),
                                new BigNumberPrefix("quadringenti", "NS"),
                                new BigNumberPrefix("quingenti",    "NS"),
                                new BigNumberPrefix("sescenti",     "N"),
                                new BigNumberPrefix("septingenti",  "N"),
                                new BigNumberPrefix("octingenti",   "MX"),
                                new BigNumberPrefix("nongenti")
                            }
                        }
                    };

        static BigNumberPrefix GetPrefix(BigNumberPrefix.Type prefixType, int value)
        {
            return value != 0 ? prefixes[prefixType][value - 1] : null;
        }

        public static string GetPrettyNumber(double number)
        {
            if (double.IsNaN(number) || double.IsInfinity(number) || Math.Abs(number) < 1e6)
            {
                return number.ToString("N2");
            }
            else
            {
                int u = 0;
                int v = 0;
                int n = (int)Math.Log10(Math.Abs(number));

                do
                {
                    u++;

                    v = n - 3 * (u + 1);
                }
                while (v >= 3);

                number /= Math.Pow(10.0, n - v);

                if (u < 10)
                {
                    return $"{number.ToString("F2")} {simpleNames[u - 1]}";
                }
                else
                {
                    int[] digits = new int[3];

                    for (int i = 0; i < 3; ++i)
                    {
                        digits[i] = u % 10;

                        u /= 10;
                    }

                    var prefixUnits = GetPrefix(BigNumberPrefix.Type.Units, digits[0]);
                    var prefixTens = GetPrefix(BigNumberPrefix.Type.Tens, digits[1]);
                    var prefixHundreds = GetPrefix(BigNumberPrefix.Type.Hundreds, digits[2]);

                    string result = string.Empty;

                    if (prefixUnits)
                    {
                        if (prefixTens)
                        {
                            prefixUnits = prefixTens.GetWithModifier(prefixUnits);

                            if (prefixHundreds)
                            {
                                result += prefixUnits + prefixTens + prefixHundreds;
                            }
                            else
                                result += prefixUnits + prefixTens.correctTens;
                        }
                        else
                        {
                            prefixUnits = prefixHundreds.GetWithModifier(prefixUnits);

                            result += prefixUnits + prefixHundreds;
                        }
                    }
                    else if (prefixTens)
                    {
                        if (prefixHundreds)
                        {
                            result += prefixTens + prefixHundreds;
                        }
                        else
                            result += prefixTens.correctTens;
                    }
                    else
                        result += prefixHundreds;

                    return $"{number.ToString("F2")} {char.ToUpper(result[0])}{result.Substring(1)}llion";
                }
            }
        }
    }
}
Аватара пользователя
1max1
Адепт
 
Сообщения: 5505
Зарегистрирован: 28 июн 2017, 10:51

Re: Именование больших чисел

Сообщение Tolking 29 июн 2020, 22:50

Ну если у тебя число нужно представлять то как-то сложно...

ns=n.toString()

nom=ns.length % 3;

if (nom==0) nom=3;

dec=(ns.length-nom)/3;

switch(dec){
1: suf="Tыс"
2: suf="Мил"
3: suf="Млрд"
4: suf="Tрлн"
.
.
.
}

return ns.left(nom)+" "+suf;
Ковчег построил любитель, профессионалы построили Титаник.
Аватара пользователя
Tolking
Адепт
 
Сообщения: 2714
Зарегистрирован: 08 июн 2009, 18:22
Откуда: Тула

Re: Именование больших чисел

Сообщение 1max1 30 июн 2020, 00:54

Можно сделать enum и просто брать из него, но там будет 100 вариантов))

Думаю, с enum будет в разы быстрее работать... спасибо за идею.
Аватара пользователя
1max1
Адепт
 
Сообщения: 5505
Зарегистрирован: 28 июн 2017, 10:51


Вернуться в Исходники (Копилка)

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 3