Spending/LargeNumberString.cs
using System;
using System.Globalization;
using System.Text;
namespace PlanetMeat;
public static class LargeNumberString
{
private const string NEGATIVE_ZERO = "-0";
private const string UNSIGNED_ZERO = "0";
private const int EXPONENT_BATCH_SIZE = 3;
private const string SCI_NOTATION_FORMAT = "0.00e0";
private static readonly string[] EXPONENT_BATCH_SUFFIXES = ["", "k", "M", "B", "T", "Q", "X", "Z"];
private static string ShiftDecimal( string value, int shiftBy )
{
var decimalPosition = value.IndexOf( '.' );
if ( decimalPosition < 0 )
return value;
StringBuilder builder = new( value );
builder.Remove( decimalPosition, 1 );
if ( shiftBy < value.Length - 1 - decimalPosition )
builder.Insert( decimalPosition + shiftBy, '.' );
return builder.ToString();
}
public static string ToSciNotation<T>( T amt ) where T : IFormattable
{
return amt.ToString( SCI_NOTATION_FORMAT, CultureInfo.InvariantCulture );
}
private static string PreventNegativeZero( string z ) => z == NEGATIVE_ZERO ? UNSIGNED_ZERO : z;
public static string ToCompact<T>( T amt, bool intifySmall = true ) where T : IFormattable
{
var scientific = ToSciNotation( amt );
var values = scientific.Split( "e" );
if ( values.Length < 2 )
return PreventNegativeZero( amt.ToString() );
var exponent = short.Parse( values[1] );
if ( intifySmall && exponent < EXPONENT_BATCH_SIZE )
return PreventNegativeZero( amt.ToString( "##0", CultureInfo.InvariantCulture ) );
if ( exponent >= EXPONENT_BATCH_SIZE * EXPONENT_BATCH_SUFFIXES.Length )
return scientific;
for ( byte i = 0; i < EXPONENT_BATCH_SUFFIXES.Length; i++ )
{
if ( exponent < EXPONENT_BATCH_SIZE * (i + 1) )
return ShiftDecimal( values[0], exponent - EXPONENT_BATCH_SIZE * i ) + EXPONENT_BATCH_SUFFIXES[i];
}
return scientific;
}
}