【问题标题】:Remove trailing zeros删除尾随零
【发布时间】:2011-05-30 09:04:23
【问题描述】:

我有一些由集合返回的字段

2.4200
2.0044
2.0000

我想要这样的结果

2.42
2.0044
2

我尝试使用 String.Format,但它返回 2.0000 并将其设置为 N0 也会对其他值进行舍入。

【问题讨论】:

  • 初始记录类型是字符串??
  • 查看我的答案:String.Format() 和 'G' 应该得到你想要的。我已经用标准数字格式的链接更新了我的答案。非常有用。
  • 可以将小数转换为double,默认的ToString 用于双精度输出尾随零。阅读this
  • 与将“G”作为字符串格式传递给ToString函数相比,它可能会花费更少的性能(大量记录之间)。
  • 您不应该将小数转换为双精度数,它会失去意义,并且可能会引入二次舍入不准确的幂。

标签: c# .net decimal


【解决方案1】:

我遇到了同样的问题,但在我无法控制输出到字符串的情况下,这是由库处理的。在查看了 Decimal 类型的实现细节之后(参见http://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx), 我想出了一个巧妙的技巧(这里作为扩展方法):

public static decimal Normalize(this decimal value)
{
    return value/1.000000000000000000000000000000000m;
}

小数的指数部分减少到所需的值。对输出小数调用 ToString() 将写入数字,不带任何尾随 0。例如

1.200m.Normalize().ToString();

【讨论】:

  • 这个答案拥有,因为与这个问题(甚至整个主题)的基本上所有其他答案不同,它实际上是有效的,并且可以满足 OP 的要求。
  • 这里的 0 的数量是准确的数量还是只是为了覆盖大多数预期值?
  • MSDN 声明“比例因子隐含地是数字 10,提高到从 0 到 28 的指数”,我理解为十进制数最多有 28 位小数点后。任何数量的零 >= 28 都可以。
  • 这是最好的答案!答案中的数字有 34 个数字,1 后跟 33 个0-s,但这会创建完全相同的 decimal 实例作为具有 29 个数字的数字,一个 1 和 28 个0-s .就像@ThomasMaterna 在他的评论中所说的那样。没有System.Decimal 可以有超过 29 个数字(前导数字大于 79228... 的数字总共只能有 28 个数字)。如果您想要 更多 个尾随零,请乘以 1.000...m 而不是除以。另外,Math.Round 可以去掉一些零,例如Math.Round(27.40000m, 2) 给出27.40m,所以只剩下一个零。
  • 很棒的技巧,但不适用于 Mono 并且不能保证在 .NET 的功能版本上工作
【解决方案2】:

如果输入的是字符串,是不是就这么简单?您可以使用以下之一:

string.Format("{0:G29}", decimal.Parse("2.0044"))

decimal.Parse("2.0044").ToString("G29")

2.0m.ToString("G29")

这应该适用于所有输入。

更新查看Standard Numeric Formats,正如文档明确指出的那样,我必须将精度说明符明确设置为 29:

但是,如果数字是 Decimal 并且省略了精度说明符,则始终使用定点表示法并保留尾随零

更新 Konrad pointed out 在 cmets 中:

注意像 0.000001 这样的值。 G29 格式将以尽可能短的方式显示它们,因此它将切换为指数符号。 string.Format("{0:G29}", decimal.Parse("0.00000001",System.Globalization.CultureInfo.GetCultureInfo("en-US"))) 将给出“1E-08”作为结果。

【讨论】:

  • 注意像 0.000001 这样的值。 G29 格式将以尽可能短的方式显示它们,因此它将切换为指数符号。 string.Format("{0:G29}", decimal.Parse("0.00000001",System.Globalization.CultureInfo.GetCultureInfo("en-US"))) 将给出“1E-08”作为结果
  • 请注意"G0" 的工作方式与"G29" 类似,因为如上所述,当精度说明符为零时,将使用该类型的默认精度(decimal 为 29)。
  • @Konrad - 有没有办法避免小数点后 5 位或 6 位数字的科学记数法?
  • @DogEars 如果我从数据库中获得 20,00000 并且我只想在我的 UI 上显示 20,00 怎么办?
  • 警告!仅当您当前的文化需要一个点作为小数分隔符时,此答案才有效。在许多文化中,该点被假定为千位分隔符,结果将是完全错误的。始终在这些转化中指定文化信息。
【解决方案3】:

我认为使用Custom Numeric Format Strings 更安全。

decimal d = 0.00000000000010000000000m;
string custom = d.ToString("0.#########################");
// gives: 0,0000000000001
string general = d.ToString("G29");
// gives: 1E-13

【讨论】:

【解决方案4】:

我使用此代码来避免使用“G29”科学记数法:

public static string DecimalToString(this decimal dec)
{
    string strdec = dec.ToString(CultureInfo.InvariantCulture);
    return strdec.Contains(".") ? strdec.TrimEnd('0').TrimEnd('.') : strdec;
}

编辑:使用系统 CultureInfo.NumberFormat.NumberDecimalSeparator :

public static string DecimalToString(this decimal dec)
{
    string sep = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
    string strdec = dec.ToString(CultureInfo.CurrentCulture);
    return strdec.Contains(sep) ? strdec.TrimEnd('0').TrimEnd(sep.ToCharArray()) : strdec;
}

【讨论】:

  • 很好,但它不适用于使用逗号作为小数分隔符的区域性。你需要使用Culture.NumberFormat.NumberDecimalSeparator
  • 最佳方法。其他一切都是不必要的复杂。请记住始终检查您的数字是否包含小数点。
  • 要作为扩展,参数前缺少“this”:public static string DecimalToString(this decimal dec)
  • 非常感谢 cmets。我已经用带有提示的新版本更新了答案。
【解决方案5】:

使用哈希 (#) 符号仅在必要时显示尾随 0。请参阅下面的测试。

decimal num1 = 13.1534545765;
decimal num2 = 49.100145;
decimal num3 = 30.000235;

num1.ToString("0.##");       //13.15%
num2.ToString("0.##");       //49.1%
num3.ToString("0.##");       //30%

【讨论】:

  • 不是答案。您不是在删除尾随零,而是在删除精度。您建议的方法num1.ToString("0.##") 应该返回13.1534545765(没有变化),因为没有任何尾随零。
  • 这正是我展示我的三个建议输入的答案的原因,以便用户可以看到我的解决方案是如何工作的。
  • 即使您列出建议的输入,它也不会回答问题。
  • @Jan'splite'K。这是一样的,因为尾随零通常会保留重要性,因此当删除它们时精度会降低。只需谷歌这个主题。
  • 完美回答了这个问题。我认为最好的答案。评论它消除了精度是正确的,但这不是问题。最重要的是——这是我需要的。我的用户通常会输入整数,可能类似于 2.5,但我需要支持小数点后 3 位数字。所以我想给他们输入的内容,而不是他们没有输入的一堆零。例如,Console.Write ($"num3={num3:0.##}"); => num3=30
【解决方案6】:

我从http://dobrzanski.net/2009/05/14/c-decimaltostring-and-how-to-get-rid-of-trailing-zeros/找到了一个优雅的解决方案

基本上

decimal v=2.4200M;

v.ToString("#.######"); // Will return 2.42. The number of # is how many decimal digits you support.

【讨论】:

  • 请注意,如果v0m,则代码的结果是空字符串而不是"0"
  • 是的。它应该是“0.########”。
  • 不错的答案,但链接已损坏——现在转到该网站的主页——因此您可能会删除它。
  • 如果您的值的小数位数多于格式字符串,您将丢失数据。 decimal test = 0.00000005m; Console.WriteLine(test.ToString("0.######"));
【解决方案7】:

一种非常低级的方法,但我相信这将是仅使用快速整数计算(并且没有慢速字符串解析和文化敏感方法)的最高效方法:

public static decimal Normalize(this decimal d)
{
    int[] bits = decimal.GetBits(d);

    int sign = bits[3] & (1 << 31);
    int exp = (bits[3] >> 16) & 0x1f;

    uint a = (uint)bits[2]; // Top bits
    uint b = (uint)bits[1]; // Middle bits
    uint c = (uint)bits[0]; // Bottom bits

    while (exp > 0 && ((a % 5) * 6 + (b % 5) * 6 + c) % 10 == 0)
    {
        uint r;
        a = DivideBy10((uint)0, a, out r);
        b = DivideBy10(r, b, out r);
        c = DivideBy10(r, c, out r);
        exp--;
    }

    bits[0] = (int)c;
    bits[1] = (int)b;
    bits[2] = (int)a;
    bits[3] = (exp << 16) | sign;
    return new decimal(bits);
}

private static uint DivideBy10(uint highBits, uint lowBits, out uint remainder)
{
    ulong total = highBits;
    total <<= 32;
    total = total | (ulong)lowBits;

    remainder = (uint)(total % 10L);
    return (uint)(total / 10L);
}

【讨论】:

【解决方案8】:

这很简单。

decimal decNumber = Convert.ToDecimal(value);
        return decNumber.ToString("0.####");

经过测试。

干杯:)

【讨论】:

    【解决方案9】:

    取决于您的数字代表什么以及您希望如何管理这些值:它是一种货币,您需要四舍五入还是截断,您是否需要这种四舍五入仅用于显示?

    如果为了显示考虑格式化数字是 x.ToString("")

    http://msdn.microsoft.com/en-us/library/dwhawy9k.aspx

    http://msdn.microsoft.com/en-us/library/0c899ak8.aspx

    如果只是舍入,请使用需要 MidPointRounding 重载的 Math.Round 重载

    http://msdn.microsoft.com/en-us/library/ms131274.aspx)

    如果您从数据库中获取价值,请考虑转换而不是转换: 双精度值 = (十进制)myRecord["columnName"];

    【讨论】:

      【解决方案10】:

      这将起作用:

      decimal source = 2.4200m;
      string output = ((double)source).ToString();
      

      或者如果你的初始值为string:

      string source = "2.4200";
      string output = double.Parse(source).ToString();
      

      关注this comment

      【讨论】:

      • @Damien,是的,请注意,对于这些目的(除非你做十亿条记录,否则你不会有任何感觉),首先是因为 double 更快,其次是因为将字符串格式传递给ToString 函数比不传递参数花费更多的性能。再说一次,除非您处理大量记录,否则您不会有任何感觉。
      • 您不必指定 G,因为 double.ToString 默认会删除尾随零。
      • 注意像 0.000001 这样的值。这些将以指数符号表示。 double.Parse("0.00000001",System.Globalization.CultureInfo.GetCultureInfo("en-US")).ToString() 将给出“1E-08”作为结果
      • 永远不要这样做。通常,您使用小数的原因是因为您精确地表示了一个数字(而不是像 double 一样)。当然,这个浮点错误可能非常小,但可能会导致显示不正确的数字,尽管如此
      • 哎呀,将 128 位数据类型(十进制)转换为 64 位数据类型(双精度)进行格式化,不是个好主意!
      【解决方案11】:

      这个怎么样:

      public static string TrimEnd(this decimal d)
          {
              string str = d.ToString();
              if (str.IndexOf(".") > 0)
              {
                  str = System.Text.RegularExpressions.Regex.Replace(str.Trim(), "0+?$", " ");
                  str = System.Text.RegularExpressions.Regex.Replace(str.Trim(), "[.]$", " ");
              }
              return str;
          }
      

      【讨论】:

        【解决方案12】:

        你可以设置为:

        decimal decNumber = 23.45600000m;
        Console.WriteLine(decNumber.ToString("0.##"));
        

        【讨论】:

        • 为此,您应该放置 28 个 '#' 字符而不是两个。
        • 是的,我误解了他的问题... =/
        【解决方案13】:

        以下代码可用于不使用字符串类型:

        int decimalResult = 789.500
        while (decimalResult>0 && decimalResult % 10 == 0)
        {
            decimalResult = decimalResult / 10;
        }
        return decimalResult;
        

        返回 789.5

        【讨论】:

          【解决方案14】:

          如果您想保留十进制数,请尝试以下示例:

          number = Math.Floor(number * 100000000) / 100000000;
          

          【讨论】:

            【解决方案15】:

            尝试对 DecimalToString (https://stackoverflow.com/a/34486763/3852139) 做更友好的解决方案:

            private static decimal Trim(this decimal value)
            {
                var s = value.ToString(CultureInfo.InvariantCulture);
                return s.Contains(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator)
                    ? Decimal.Parse(s.TrimEnd('0'), CultureInfo.InvariantCulture)
                    : value;
            }
            
            private static decimal? Trim(this decimal? value)
            {
                return value.HasValue ? (decimal?) value.Value.Trim() : null;
            }
            
            private static void Main(string[] args)
            {
                Console.WriteLine("=>{0}", 1.0000m.Trim());
                Console.WriteLine("=>{0}", 1.000000023000m.Trim());
                Console.WriteLine("=>{0}", ((decimal?) 1.000000023000m).Trim());
                Console.WriteLine("=>{0}", ((decimal?) null).Trim());
            }
            

            输出:

            =>1
            =>1.000000023
            =>1.000000023
            =>
            

            【讨论】:

              【解决方案16】:

              这是我写的一个扩展方法,如果它是最后一个字符(在删除零之后),它也会删除 dotcomma

              public static string RemoveZeroTail(this decimal num)
              {
                  var result = num.ToString().TrimEnd(new char[] { '0' });
                  if (result[result.Length - 1].ToString() == "." || result[result.Length - 1].ToString() == ",")
                  {
                      return result.Substring(0, result.Length - 1);
                  }
                  else
                  {
                      return result;
                  }
              }
              

              【讨论】:

                【解决方案17】:

                试试这个代码:

                string value = "100";
                value = value.Contains(".") ? value.TrimStart('0').TrimEnd('0').TrimEnd('.') : value.TrimStart('0');
                

                【讨论】:

                • 不使用'.'的区域怎么办?
                • 与本文中的其他方法相比根本不是一种选择。转换为字符串并返回始终是操作数字的不好选择(至少因为语言环境)
                【解决方案18】:

                非常简单的答案是使用 TrimEnd()。这是结果,

                double value = 1.00;
                string output = value.ToString().TrimEnd('0');
                

                输出为 1 如果我的值为 1.01,那么我的输出将为 1.01

                【讨论】:

                • 如果value = 100呢?
                • @Raj De Inno 即使值为 1.00,它给出的输出也是“1”。不是“1”
                【解决方案19】:

                这样试试

                string s = "2.4200";
                
                s = s.TrimStart("0").TrimEnd("0", ".");
                

                然后将其转换为浮点数

                【讨论】:

                • 您可以使用subString() 方法,但根据此处发布的问题,我没有看到任何这样的要求 =)
                • 不使用'.'的区域怎么办
                • 这对于 10.0 的偶数倍数的数字非常失败。
                • @BenVoigt 所说的完全正确,例如 "12300.00" 将被修剪为 "123"。另一个似乎不受欢迎的效果是某些数字,例如 "0.00" 被一直修剪到空字符串 ""(尽管这个数字 是整数“10.0 的倍数”)。
                猜你喜欢
                • 2012-01-26
                • 1970-01-01
                • 1970-01-01
                • 2018-09-28
                • 2019-02-03
                • 2012-07-01
                • 2012-07-02
                • 2020-09-11
                • 1970-01-01
                相关资源
                最近更新 更多