有效转换特定值的方法

时间:2014-06-03 20:24:28

标签: c# .net parsing casting rounding

所以我想说我有一个字符串数组:

string[] values = new[] { "1", "2", "3", "1.5", "56.5", "8" };

让我们说我必须遍历这些值并执行以下操作:

  1. 将它舍入到最近的偶数(如果它只是一个双数)

  2. 舍入后删除数字的小数部分。

  3. 如果数字为负数,请删除标志。

  4. string[] values = new[] { "1", "2", "3", "1.5", "56.5", "8" };
    
    for (int i = 0; i < values.Length; i++)
    {
        file[i].Value = ((Int32)Math.Abs(Math.Round(Double.Parse(values[i]), MidpointRounding.ToEven))).ToString();
    }
    

    与执行此操作基本相同:

    string[] values = new[] { "1", "2", "3", "1.5", "56.5", "8" };
    
    for (int i = 0; i < values.Length; i++)
    {
        String strValue = values[j];
        Double dblValue = Double.Parse(strValue);
        Double dblRoundedValue = Double.Parse(dblValue);
        Int32 intValue = (Int32)dblRoundedValue;
        Int32 intAbsValue = Math.Abs(intValue);
        String finalValue = intAbsValue.ToString();
    
        file[i].Value = finalValue;
    }
    

    该阵列中可能有超过一百万个值,所以有没有办法让这个过程更有效?

5 个答案:

答案 0 :(得分:5)

此操作本质上是可并行化的(如果这是一个单词)。 Parallel.ForEach循环,并行Linq管道或类似的东西会缩短执行时间。

string[] values = new[] { "1", "2", "3", "1.5", "56.5", "8" };

var file = values.AsParallel()
                 .Select(s => Double.Parse(s))
                 .Select(d => (int)Math.Round(d))
                 .Select(i => Math.Abs(i).ToString())
                 .ToArray();

答案 1 :(得分:4)

嗯,我尝试了一些东西,并且在考虑是否有必要将其解析为Double之前,使用了超新星的建议来检查字符串中的小数点,以及Andrew Cooper的建议使用Parallel.For我得到

的结果
Init...Done
Simple
20024
LookingAtString
8082
ParallelConvertLookingAtString
3559
Simple
19552
LookingAtString
7985
ParallelConvertLookingAtString
3595

使用以下代码...

using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        const int nNumbers = 20000000;

        static string[] values = new string[nNumbers];
        static string[] file = new string[nNumbers];

        static Random rand = new Random();

        // Create some sample data.
        static void Init()
        {
            string sgn = "";
            for (int i = 0; i <= nNumbers - 1; i++)
            {
                sgn = rand.Next(51) == 1 ? "-" : "";

                if (rand.Next(4) == 1)
                {
                    values[i] = sgn + (rand.NextDouble() * 100).ToString();
                }
                else
                {
                    values[i] = sgn + rand.Next(100);
                }
            }
        }

        static void ConvertSimple()
        {
            for (int i = 0; i <= nNumbers - 1; i++)
            {
                file[i] = Math.Abs(Math.Round(double.Parse(values[i]), MidpointRounding.ToEven)).ToString();
            }
        }

        static void ConvertLookingAtString()
        {
            for (int i = 0; i <= nNumbers - 1; i++)
            {
                if (values[i].IndexOf('.') >= 0)
                {
                    file[i] = Math.Abs(Math.Round(double.Parse(values[i]), MidpointRounding.ToEven)).ToString();
                }
                else
                {
                    file[i] = values[i].TrimStart('-');
                }

            }

        }

        static void ParallelConvertLookingAtString()
        {
            Parallel.For(0, nNumbers, i =>
            {
                if (values[i].IndexOf('.') >= 0)
                {
                    file[i] = Math.Abs(Math.Round(double.Parse(values[i]), MidpointRounding.ToEven)).ToString();
                }
                else
                {
                    file[i] = values[i].TrimStart('-');
                }

            });

        }

        static void Main()
        {
            Console.Write("Init...");
            Init();
            Console.WriteLine("Done");

            Stopwatch sw = new Stopwatch();

            // run each test twice

            for (int testNum = 0; testNum < 2; testNum++)
            {
                sw.Reset();
                sw.Start();
                ConvertSimple();
                sw.Stop();
                Console.WriteLine("Simple\n" + sw.ElapsedMilliseconds.ToString());

                sw.Reset();
                sw.Start();
                ConvertLookingAtString();
                sw.Stop();
                Console.WriteLine("LookingAtString\n" + sw.ElapsedMilliseconds.ToString());

                sw.Reset();
                sw.Start();
                ParallelConvertLookingAtString();
                sw.Stop();
                Console.WriteLine("ParallelConvertLookingAtString\n" + sw.ElapsedMilliseconds.ToString());
            }

            Console.ReadLine();

        }
    }
}

请注意,这是使用了两千万个样本,而不是你建议的一百万个样本,大约三分之一的值是分数,大约百分之二是负数。你没有给出那些预期的分数,所以我做了一些事情。

有些位可能是非最佳C#,因为我是从VB转换的。在具有6GB RAM的Intel Core i7 920上运行,编译为在x64上运行。

编辑:是的,所以我的回答是上面的ParallelConvertLookingAtString方法。

答案 2 :(得分:1)

Double dblValue;
Double dblRoundedValue;
Int32 intValue;
Int32 intAbsValue;
String finalValue;
for (int i = 0; i < values.Length; i++)
{
    strValue = values[j];
    if (!Int32.TryParse(strValue, out intValue))
    {
        //dblValue = Double.Parse(strValue);
        //dblRoundedValue = Double.Parse(dblValue);
        //intValue = (Int32)dblRoundedValue;
        intValue = (Int32)(Double.Parse(strValue));
    }
    //intValue  = Math.Abs(intValue);
    //finalValue = intValue .ToString();

    file[i].Value = (Math.Abs(intValue)).ToString();
}

但我不明白两个Double.Parse 如果你需要,请把它们放回去 并将其转换为并行

ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 16;
Parallel.For(0, values.Length, parallelOptions, i =>
{
   strValue = values[j];
   if (!Int32.TryParse(strValue, out intValue))
   {
      intValue = (Int32)(Double.Parse(strValue));
   }
   if (intValue < 0) intValue = -intValue;
   file[i].Value = intValue.ToString();
});

我认为垃圾将是最快的

string[] values = new string[] { "1", "2", "3", "1.5", "56.5", "8" };
string[] files = new string[values.Length];
HashSet<char> lt5 = new HashSet<char> {'0','1','2','3','4'};
bool haveDecimal;
bool haveDecimalConfirmed;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < values.Length; i++)
{
    sb.Clear();
    haveDecimal = false;
    haveDecimalConfirmed = false;
    foreach(char c in values[i])
    {
        if (haveDecimal)
        {
            if (lt5.Contains(c))
            {
                files[i] = sb.ToString();
            }
            else
            {
                files[i] = (Int32.Parse(sb.ToString()) + 1).ToString();
            }
            haveDecimalConfirmed = true;
            break;
        }
        else if (c == '.')
        {
            haveDecimal = true;
            continue;
        }
        if (c == '-') continue;
        sb.Append(c);
    }
    if (!haveDecimalConfirmed) files[i] = sb.ToString();
}

答案 3 :(得分:1)

一些想法:

  1. 如果您有一百万个值并且您期望重复次数很多,那么您可以构建一个可以重复使用的先前结果的哈希表。
  2. 我对此进行了测试,如果你说所有元素都相同,它会快得多。少数不同的元素可能会更快。但是如果元素都不同,它会占用大量内存,而且一旦Hash变得太大,你就会想要限制它。

    1. 如果您确定格式化区域设置,则可以执行以下操作:
    2. 字符串是一个字符数组。而不是转换为双倍,迭代字符直到你到达第一个&#34;。&#34;然后采取下一个数字。转换文本(忽略任何 - )之前。到整数。如果之后的值。是5,6,7,8,9然后将1加到该整数。

      示例:

      Input: "-1002.55"
      
      Text between - and . is: "1002"
      Converted to int this is: 1002
      Character immediately after . is: "5"
      Result: 1002 + 1 = 1003
      

      那是圆到最近的。如果您需要舍入到最接近的偶数,请查看之前的数字。如果之后有一个,以及之前的数字。是1,3,5,7,9然后添加到最终的数字。

答案 4 :(得分:-3)

假设你是指&#34;偶数&#34;表示&#34;最接近的整数&#34;而不是&#34; 2&#34;的整数倍。 假设您的意思是从所有数字中删除负号,而不是仅使用舍入的数字。

您认为您的输入字符串始终有效Double格式,这是否合理?假设没有使用科学记数法格式化的数字,请检查每个输入字符串的小数点,它比解析更快,并测试小数部分。没有小数点,不需要舍入。