Advertisement
Guest User

Human Number Formatting Example

a guest
Feb 13th, 2013
84
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 12.21 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. namespace numtst
  7. {
  8.     class Program
  9.     {
  10.         static void Main(string[] args)
  11.         {
  12.             Console.WriteLine(Format(3.343e100, 0.05)); // 33 followed by 99 zeroes
  13.             Console.WriteLine(Format(12.3456, 0.05)); // 12
  14.             Console.WriteLine(Format(12.3456, 0.02)); // 12 and 1/3rd
  15.             Console.WriteLine(Format(12.3456, 0.0005)); // 12.35
  16.             Console.WriteLine(Format(12.3456, 0.0001)); // 12.346
  17.             Console.WriteLine(Format(3.343e10, 0.05)); // 33 billion
  18.             Console.WriteLine(Format(3.343e10, 0.01)); // 33 and 3/7ths billion
  19.             Console.WriteLine(Format(3.343e15, 0.01)); // 3 and 1/3rd million billion
  20.             Console.WriteLine(Format(3.343e15, 0.001)); // 3.34 million billion
  21.             Console.WriteLine(Format(3.343e15, 0.0001)); // 3.343 million billion
  22.             Console.ReadKey();
  23.         }
  24.  
  25.         // This is the heart of our algorithm.
  26.         static string Format(double num, double prefMaxError)
  27.         {
  28.             double absNum = Math.Abs(num);
  29.             if (absNum < 0.00001) return "0";
  30.  
  31.  
  32.             // We try a lot of things.
  33.             List<FormatResult> list = new List<FormatResult>();
  34.             FixedPoint0(absNum, list, 100);
  35.             Fraction(absNum, list, 200);
  36.             FixedPoint0Postfix(absNum, list, 230);
  37.             FractionPostfix(absNum, list, 240);
  38.             FixedPoint1Postfix(absNum, list, 250);
  39.             FixedPoint2Postfix(absNum, list, 260);
  40.             FixedPoint3Postfix(absNum, list, 270);
  41.             FixedPoint1(absNum, list, 300);
  42.             FixedPoint2(absNum, list, 400);
  43.             FixedPoint3(absNum, list, 500);
  44.             FollowedByZeroes(absNum, list, 0, 550);
  45.             FollowedByZeroes(absNum, list, 1, 600);
  46.             FollowedByZeroes(absNum, list, 2, 700);
  47.             FollowedByZeroes(absNum, list, 3, 800);
  48.             FollowedByZeroes(absNum, list, 4, 800);
  49.             // Off-by-one not yet implimented...
  50.             // Testing for nice postfix fractions like "1/3rd million" not implemented...
  51.             // Billionths and billionths of billionths not implemented.
  52.  
  53.             // If we couldn't find any acceptable representation give up.
  54.             if (list.Count == 0)
  55.                 return "Non-representable number.";
  56.  
  57.             list.Sort(delegate(FormatResult a, FormatResult b)
  58.             {
  59.                 return a.Priority.CompareTo(b.Priority);
  60.             });
  61.  
  62.             // We select the best priority item that meets our relative error.
  63.             foreach (FormatResult fr in list)
  64.             {
  65.                 double relError = Math.Abs((fr.ActualValue - absNum) / fr.ActualValue);
  66.                 if (relError < prefMaxError)
  67.                     return Finalize(num, fr);
  68.             }
  69.  
  70.             // Nothing found still... Return the most accurate instead.
  71.  
  72.             list.Sort(delegate(FormatResult a, FormatResult b)
  73.             {
  74.                 double relErrorA = Math.Abs((a.ActualValue - absNum) / a.ActualValue);
  75.                 double relErrorB = Math.Abs((b.ActualValue - absNum) / b.ActualValue);
  76.                 if (relErrorA == relErrorB)
  77.                     return a.Priority.CompareTo(b.Priority);
  78.                 return relErrorA.CompareTo(relErrorB);
  79.             });
  80.  
  81.             return Finalize(num, list[0]);
  82.         }
  83.  
  84.         class FormatResult
  85.         {
  86.             public double ActualValue;
  87.             public string Representation;
  88.             public bool WrittenNegative;
  89.             public int Priority;
  90.             public FormatResult(double av, string rep, bool wn, int pr)
  91.             {
  92.                 ActualValue = av;
  93.                 Representation = rep;
  94.                 WrittenNegative = wn;
  95.                 Priority = pr;
  96.             }
  97.         }
  98.  
  99.         // Brute forces most accurate simple fraction
  100.         static string FractionCore(double num, out double outActual, out bool negType)
  101.         {
  102.             outActual = 0;
  103.             negType = false;
  104.  
  105.             // We consider integer parts greater than this value
  106.             // not needing of fractional parts.
  107.             int maxThreshold = 1000;
  108.             if (num >= maxThreshold) return null;
  109.  
  110.             // Brute force
  111.             double bestDiff = 10;
  112.             int bestD = -1;
  113.             int bestN = 0;
  114.             for (int denom = 2; denom <= 10; denom++)
  115.             {
  116.                 int n = (int)Math.Round(num * denom);
  117.                 double actual = n / (double)denom;
  118.                 double diff = Math.Abs(actual - num);
  119.                 if (diff < bestDiff)
  120.                 {
  121.                     bestDiff = diff;
  122.                     bestD = denom;
  123.                     bestN = n;
  124.                 }
  125.             }
  126.  
  127.             if (bestD < 0) return null;
  128.  
  129.             // Make a string out of the best result.
  130.             int bestInt = bestN / bestD;
  131.             int bestNum = bestN % bestD;
  132.             int bestDen = bestD;
  133.  
  134.             if (bestInt >= maxThreshold) return null;
  135.  
  136.             string rep = "";
  137.             if (bestInt > 0)
  138.             {
  139.                 rep += bestInt.ToString();
  140.                 if (bestNum > 0)
  141.                     rep += " and ";
  142.             }
  143.             if (bestNum > 0)
  144.             {
  145.                 rep += bestNum.ToString();
  146.                 rep += "/";
  147.                 rep += bestDen.ToString();
  148.                 if (bestDen == 2) ;
  149.                 else if (bestDen == 3) rep += "rd";
  150.                 else rep += "th";
  151.                 if (bestDen != 2 && bestNum > 1) rep += "s";
  152.             }
  153.  
  154.             if (bestInt == 0 && bestNum == 0) rep = "0";
  155.  
  156.             double bestActual = (double)bestN / bestD;
  157.  
  158.             outActual = bestActual;
  159.             negType = bestInt > 0 && bestNum > 0;
  160.  
  161.             return rep;
  162.         }
  163.  
  164.         static void Fraction(double num, List<FormatResult> list, int priority)
  165.         {
  166.             bool negType;
  167.             double actual;
  168.             string r = FractionCore(num, out actual, out negType);
  169.             if (r == null) return;
  170.             list.Add(new FormatResult(actual, r, negType, priority));
  171.         }
  172.  
  173.         static void FixedPoint0(double num, List<FormatResult> list, int priority)
  174.         {
  175.             int maxThreshold = 1000000;
  176.             if (num > maxThreshold) num = maxThreshold;
  177.             int actual = (int)Math.Round(num);
  178.             string rep = string.Format("{0:#,0}", actual);
  179.             list.Add(new FormatResult(actual, rep, false, priority));
  180.         }
  181.  
  182.         static void FixedPoint1(double num, List<FormatResult> list, int priority)
  183.         {
  184.             int maxThreshold = 1000000;
  185.             if (num > maxThreshold) num = maxThreshold;
  186.             int actual = (int)Math.Round(num, 1);
  187.             string rep = string.Format("{0:#,0.0}", actual);
  188.             list.Add(new FormatResult(actual, rep, false, priority));
  189.         }
  190.  
  191.         static void FixedPoint2(double num, List<FormatResult> list, int priority)
  192.         {
  193.             int maxThreshold = 1000000;
  194.             if (num > maxThreshold) num = maxThreshold;
  195.             int actual = (int)Math.Round(num, 2);
  196.             string rep = string.Format("{0:#,0.00}", actual);
  197.             list.Add(new FormatResult(actual, rep, false, priority));
  198.         }
  199.  
  200.         static void FixedPoint3(double num, List<FormatResult> list, int priority)
  201.         {
  202.             int maxThreshold = 1000000;
  203.             if (num > maxThreshold) num = maxThreshold;
  204.             int actual = (int)Math.Round(num, 3);
  205.             string rep = string.Format("{0:#,0.000}", actual);
  206.             list.Add(new FormatResult(actual, rep, false, priority));
  207.         }
  208.  
  209.         static string PreprocessPostfix(double num, out double start, out double outMult)
  210.         {
  211.             start = 0;
  212.             outMult = 1;
  213.  
  214.             int maxPostfixes = 3;
  215.  
  216.             int numBillions = 0;
  217.             bool million = false;
  218.             bool thousand = false;
  219.  
  220.             int numPostfixes = 0;
  221.  
  222.             double mult = 1;
  223.  
  224.             while (num > 1000000000 * mult)
  225.             {
  226.                 numPostfixes++;
  227.                 numBillions++;
  228.                 mult *= 1000000000;
  229.             }
  230.  
  231.             if (num > 1000000 * mult)
  232.             {
  233.                 numPostfixes++;
  234.                 million = true;
  235.                 mult *= 1000000;
  236.             }
  237.  
  238.             if (num > 1000 * mult)
  239.             {
  240.                 numPostfixes++;
  241.                 thousand = true;
  242.                 mult *= 1000;
  243.             }
  244.  
  245.             num /= mult;
  246.  
  247.             if (numPostfixes > maxPostfixes) return null;
  248.  
  249.             start = num;
  250.             outMult = mult;
  251.  
  252.             string res = "";
  253.             if (thousand) res += " thousand";
  254.             if (million) res += " million";
  255.             for (int i = 0; i < numBillions; i++) res += " billion";
  256.  
  257.             return res;
  258.         }
  259.  
  260.         static void FractionPostfix(double num, List<FormatResult> list, int priority)
  261.         {
  262.             double mult;
  263.             string postfix = PreprocessPostfix(num, out num, out mult);
  264.             if (postfix == null) return;
  265.             double actual;
  266.             bool negType;
  267.             string rep = FractionCore(num, out actual, out negType) + postfix;
  268.             if (rep == null) return;
  269.             list.Add(new FormatResult(actual * mult, rep, true, priority));
  270.         }
  271.  
  272.         static void FixedPoint0Postfix(double num, List<FormatResult> list, int priority)
  273.         {
  274.             double mult;
  275.             string postfix = PreprocessPostfix(num, out num, out mult);
  276.             if (postfix == null) return;
  277.             string rep = string.Format("{0:#,0}", num) + postfix;
  278.             num = Math.Round(num);
  279.             list.Add(new FormatResult(num * mult, rep, true, priority));
  280.         }
  281.  
  282.         static void FixedPoint1Postfix(double num, List<FormatResult> list, int priority)
  283.         {
  284.             double mult;
  285.             string postfix = PreprocessPostfix(num, out num, out mult);
  286.             if (postfix == null) return;
  287.             string rep = string.Format("{0:#,0.0}", num) + postfix;
  288.             num = Math.Round(num, 1);
  289.             list.Add(new FormatResult(num * mult, rep, true, priority));
  290.         }
  291.  
  292.         static void FixedPoint2Postfix(double num, List<FormatResult> list, int priority)
  293.         {
  294.             double mult;
  295.             string postfix = PreprocessPostfix(num, out num, out mult);
  296.             if (postfix == null) return;
  297.             string rep = string.Format("{0:#,0.00}", num) + postfix;
  298.             num = Math.Round(num, 2);
  299.             list.Add(new FormatResult(num * mult, rep, true, priority));
  300.         }
  301.  
  302.         static void FixedPoint3Postfix(double num, List<FormatResult> list, int priority)
  303.         {
  304.             double mult;
  305.             string postfix = PreprocessPostfix(num, out num, out mult);
  306.             if (postfix == null) return;
  307.             string rep = string.Format("{0:#,0.000}", num) + postfix;
  308.             num = Math.Round(num, 3);
  309.             list.Add(new FormatResult(num * mult, rep, true, priority));
  310.         }
  311.  
  312.         static void FollowedByZeroes(double num, List<FormatResult> list, int numDigits, int priority)
  313.         {
  314.             if (num < 1) return;
  315.             int numZeroes = (int)Math.Log10(num);
  316.             if (numZeroes < numDigits) return;
  317.             double mult = Math.Pow(10.0, numZeroes - numDigits);
  318.             num /= mult;
  319.             int digits = (int)Math.Round(num);
  320.             string rep = string.Format("{0:#,0}", digits);
  321.             int nz = numZeroes - numDigits;
  322.             rep += " followed by " + nz.ToString();
  323.             if (nz == 1)
  324.                 rep += " zero";
  325.             else
  326.                 rep += " zeroes";
  327.             double actual = digits * mult;
  328.             list.Add(new FormatResult(actual, rep, false, priority));
  329.         }
  330.  
  331.         static string Finalize(double num, FormatResult fr)
  332.         {
  333.             if (num >= 0) return fr.Representation;
  334.             if (fr.WrittenNegative)
  335.                 return "negative " + fr.Representation;
  336.             return "-" + fr.Representation;
  337.         }
  338.     }
  339. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement