2

I'm somewhat new to working with BigIntegers and have tried some stuff to get this system working, but feel a little stuck at the moment and would really appreciate a nudge in the right direction or a solution.

I'm currently working on a system which reduces BigInteger values down to a more readable form, and this is working fine with my current implementation, but I would like to further expand on it to get decimals implemented.

To better give a picture of what I'm attempting, I'll break it down.

In this context, we have a method which is taking a BigInteger, and returning it as a string:

public static string ShortenBigInt (BigInteger moneyValue)

With this in mind, when a number such as 10,000 is passed to this method, 10k will be returned. Same for 1,000,000 which will return 1M.

This is done by doing:

for(int i = 0; i < prefixes.Length; i++)
{
     if(!(moneyValue >= BigInteger.Pow(10, 3*i)))
     {
         moneyValue = moneyValue / BigInteger.Pow(10, 3*(i-1));

         return moneyValue + prefixes[i-1];
     }
 }

This system is working by grabbing a string from an array of prefixes and reducing numbers down to their simplest forms and combining the two and returning it when inside that prefix range.

So with that context, the question I have is: How might I go about returning this in the same way, where passing 100,000 would return 100k, but also doing something like 1,111,111 would return 1.11M?

Currently, passing 1,111,111M returns 1M, but I would like that additional .11 tagged on. No more than 2 decimals.

My original thought was to convert the big integer into a string, then chunk out the first few characters into a new string and parse a decimal in there, but since prefixes don't change until values reach their 1000th mark, it's harder to tell when to place the decimal place.

My next thought was using BigInteger.Log to reduce the value down into a decimal friendly number and do a simple division to get the value in its decimal form, but doing this didn't seem to work with my implementation.

This system should work for the following prefixes, dynamically:

k, M, B, T, qd, Qn, sx, Sp, O, N, de, Ud, DD, tdD, qdD, QnD, sxD, SpD, OcD, NvD, Vgn, UVg, DVg, TVg, qtV, QnV, SeV, SPG, OVG, NVG, TGN, UTG, DTG, tsTG, qtTG, QnTG, ssTG, SpTG, OcTG, NoTG, QdDR, uQDR, dQDR, tQDR, qdQDR, QnQDR, sxQDR, SpQDR, OQDDr, NQDDr, qQGNT, uQGNT, dQGNT, tQGNT, qdQGNT, QnQGNT, sxQGNT, SpQGNT, OQQGNT, NQQGNT, SXGNTL

Would anyone happen to know how to do something like this? Any language is fine, C# is preferable, but I'm all good with translating. Thank you in advance!

  • 2
    This is not really a BigInt question. And it appears ToString() might have support for it: https://stackoverflow.com/a/12331610/3346583 – Christopher Feb 10 '20 at 23:02
  • 1
    Does this answer your question? [String Format Numbers Thousands 123K, Millions 123M, Billions 123B](https://stackoverflow.com/questions/11731996/string-format-numbers-thousands-123k-millions-123m-billions-123b) – Simply Ged Feb 10 '20 at 23:19

2 Answers2

3

formatting it manually could work a bit like this:

(prefixes as a string which is an char[])

public static string ShortenBigInt(BigInteger moneyValue)
{
    string prefixes = " kMGTP";
    double m2 = (double)moneyValue;

    for (int i = 1; i < prefixes.Length; i++)
    {
        var step = Math.Pow(10, 3 * i);
        if (m2 / step < 1000)
        {
            return String.Format("{0:F2}", (m2/step)) + prefixes[i];    
        }
    }
    return "err";
}
Falco Alexander
  • 3,092
  • 2
  • 20
  • 39
  • So this solution works - sort of. Because you are doing a cast to double here, we are making the use of the big integers here useless. This implementation will NOT allow for the high values I am looking for. Is there a way to do it without the double cast? – Justice Shultz Feb 11 '20 at 02:03
  • right, it will work until `double.MaxValue = 1.79769313486232E+308` which is pretty big but you are looking for more than 308 digit integers? If yes, then you will probably succeed with a string operation like taking the 5 leftmost digits and then assigning the prefix by checking the string.Length % 3. I'll provide an example later – Falco Alexander Feb 11 '20 at 07:40
0

Although Falco's answer does work, it doesn't work for what was requested. This was the solution I was looking for and received some help from a friend on it. This solution will go until there are no more prefixes left in your string array of prefixes. If you do run out of bounds, the exception will be thrown and handled by returning "Infinity".

This solution is better due to the fact there is no crunch down to doubles/decimals within this process. This solution does not have a number cap, only limit is the amount of prefixes you make/provide.

public static string ShortenBigInt(BigInteger moneyValue)
{
    if (moneyValue < 1000)
        return "" + moneyValue;

    try
    {
        string moneyAsString = moneyValue.ToString();
        string prefix = prefixes[(moneyAsString.Length - 1) / 3];

        BigInteger chopAmmount = (moneyAsString.Length - 1) % 3 + 1;

        int insertPoint = (int)chopAmmount;

        chopAmmount += 2;

        moneyAsString = moneyAsString.Remove(Math.Min(moneyAsString.Length - 1, (int)chopAmmount));
        moneyAsString = moneyAsString.Insert(insertPoint, ".");

        return moneyAsString + " " + prefix;
    }
    catch (Exception exceptionToBeThrown)
    {   
        return "Infinity";
    }
}