0

Our scientific application allows the user to configure the number of significant figures and decimal places to use when displaying numeric values. The formatting is currently being done with this code:-

var sigFigFormatted = valueToConvert.ToString("G" + numberOfSigFigs);
var theFullyFormattedValue = Convert.ToDouble(sigFigFormatted)
                                 .ToString("F" + numberOfDecimalPlaces);

I don't like all this conversion to/from strings, and can't help thinking there must be a more efficient solution?

Andrew Stephens
  • 9,413
  • 6
  • 76
  • 152
  • 2
    "efficient" from what perspective? – zerkms Sep 11 '13 at 12:26
  • @zerkms our app display's 100,000s of numeric values in the UI, and calling the above code so many times is showing up as a small bottleneck in performance analyzer. I guess the calls to ToString() and Convert() are hurting performance, so I wondered if there was a "slicker" way to do this, e.g. using custom format strings. – Andrew Stephens Sep 11 '13 at 12:55
  • But you aren't showing all these at the same time, right? Can't really see why you would need to show 100 000 numbers at the same time, or how it could fit on screen. Can't you just fix UI so it doesn't show all these at the same time? Otherwise you would have to show more code so we can see more of what it is you try to do and where you fetch the different values from and how you present the data – Daniel MesSer Sep 11 '13 at 13:22
  • You can always store the formatted value side-by-side with the numeric. Sure it violates normalization rules, but this is a good example of breaking rules for a specific performance gain. Just make sure to shield the rest of the app from this duplicity. – Keith Payne Sep 11 '13 at 13:29
  • @DanielMesSer the numeric results are being displayed in various data grids. – Andrew Stephens Sep 12 '13 at 08:16

1 Answers1

1

Look at accepted answer for this question.

I've ported code from the accepted answer to C# and made some tests. Code:

using System;
using System.Diagnostics;
using System.Linq;

namespace ConsoleApplication1
{
    public static class NumericExtensions
    {
        public static double RoundToSignificantFigures(this double num, int n)
        {
            if (num == 0)
            {
                return 0;
            }

            double magnitude = Math.Pow(10, n - (int)Math.Ceiling(Math.Log10(Math.Abs(num))));
            double shifted = Math.Round(num * magnitude);
            return shifted / magnitude;
        }

        public static double RoundToSignificantFiguresWithConvert(this double num, int n)
        {
            var sigFigFormatted = num.ToString("G" + n.ToString());
            return Convert.ToDouble(sigFigFormatted);
        }
    }

    class Program
    {
        static string[] Test1(double[] numbers, int numberOfSigFigs, int numberOfDecimalPlaces)
        {
            var result = new string[numbers.Length];
            for (int i = 0; i < numbers.Length; i++)
            {
                result[i] = numbers[i].RoundToSignificantFigures(numberOfSigFigs).ToString("F" + numberOfDecimalPlaces.ToString());
            }
            return result;
        }

        static string[] Test2(double[] numbers, int numberOfSigFigs, int numberOfDecimalPlaces)
        {
            var result = new string[numbers.Length];
            for (int i = 0; i < numbers.Length; i++)
            {
                result[i] = numbers[i].RoundToSignificantFiguresWithConvert(numberOfSigFigs).ToString("F" + numberOfDecimalPlaces.ToString());
            }
            return result;
        }

        static void Main(string[] args)
        {
            // create an array or random numbers 
            var rng = new Random();
            var numbers = new double[100000];
            for (int i = 0; i < numbers.Length; i++)
            {
                numbers[i] = 10000000000000000000D * (rng.NextDouble() - 0.5D);
            }

            const int numberOfSigFigs = 3;
            const int numberOfDecimalPlaces = 3;

            // make first run without time measurement
            Test1(numbers, numberOfSigFigs, numberOfDecimalPlaces);
            Test2(numbers, numberOfSigFigs, numberOfDecimalPlaces);

            const int numberOfIterations = 100;
            var sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < numberOfIterations; i++)
            {
                Test1(numbers, numberOfSigFigs, numberOfDecimalPlaces);
            }
            sw.Stop();
            Console.WriteLine("Test1 elapsed {0} ms", sw.ElapsedMilliseconds.ToString());

            sw.Restart();
            for (int i = 0; i < numberOfIterations; i++)
            {
                Test2(numbers, numberOfSigFigs, numberOfDecimalPlaces);
            }
            sw.Stop();
            Console.WriteLine("Test2 elapsed {0} ms", sw.ElapsedMilliseconds.ToString());

            Console.ReadKey();
        }

    }

}

Results:

Test1 elapsed 7259 ms
Test2 elapsed 12918 ms

So NumericExtensions.RoundToSignificantFigures shows more efficient way of formating numbers.

Community
  • 1
  • 1
Alexander Simonov
  • 1,564
  • 1
  • 9
  • 15