使用自定义分组和十进制分隔符将数字格式化为字符串,而不更改精度



我想在C#中用自定义的组/千分隔符和十进制分隔符将一些数字格式化为字符串。组和小数分隔符可以根据用户输入进行更改,因此我希望使用NumberFormatInfo对象,而不是硬编码的格式字符串。我下面的代码使用了正确的分隔符,但它将数字的精度更改为始终为小数点后两位,而我希望保持数字的完全精度,并且只在需要时显示小数点(因此整数值没有小数点)。

我怎样才能做到这一点?我想我需要更改"N"参数,但将其更改为什么?

double n1 = 1234;
double n2 = 1234.5;
double n3 = 1234567.89;
double n4 = 1234.567;
var nfi = new NumberFormatInfo();
nfi.NumberDecimalSeparator = ",";
nfi.NumberGroupSeparator = " ";
string s1 = n1.ToString("N", nfi); //want "1 234" but I get "1 234,00"
string s2 = n2.ToString("N", nfi); //want "1 234,5" but I get "1 234,50"
string s3 = n3.ToString("N", nfi); //correct output of "1 234 567,89" 
string s4 = n4.ToString("N", nfi); //want " 1 234,567" but I get "1 234,57"

下面是我作为扩展方法提出的解决方案。

public static string Format(this double d, NumberFormatInfo numberFormatInfo)
{
    string s = d.ToString(CultureInfo.InvariantCulture);
    int index = s.IndexOf('.');
    int decimalPlaces = index == -1 ? 0 : s.Length - index - 1;
    return d.ToString($"N{decimalPlaces}", numberFormatInfo);
}

编辑:

一种使用内置ToString()(通过使用数字占位符#Replace及/或Trim:的解决方法

double n1 = 1234;
double n2 = 1234.5;
double n3 = 1234567.89;
double n4 = 1234.567;
string s1 = n1.ToString("### ### ###.###").Replace(".",",").Trim();
string s2 = n2.ToString("### ### ###.###").Replace(".", ",").Trim();
string s3 = n3.ToString("### ### ###.###").Replace(".", ",").Trim();
string s4 = n4.ToString("### ### ###.###").Replace(".", ",").Trim();

将其与数字格式相结合以读取,作为小数分隔符是不起作用的:

double n1 = 1234;
double n2 = 1234.5;
double n3 = 1234567.89;
double n4 = 1234.567;
var nfi = new NumberFormatInfo();
nfi.NumberDecimalSeparator = ",";
nfi.NumberGroupSeparator = " ";
string s1 = n1.ToString("### ### ###,###", nfi); //doesn't work
string s2 = n2.ToString("### ### ###,###", nfi);
string s3 = n3.ToString("### ### ###,###", nfi);
string s4 = n4.ToString("### ### ###,###", nfi);

原件:

我想你不能只使用内置的ToString()来获得正确的结果。你可能需要一些LINQ技巧才能得到你想要的结果:

double n1 = 1234;
double n2 = 1234.5;
double n3 = 1234567.89;
double n4 = 1234.567;
var nfi = new NumberFormatInfo();
nfi.NumberDecimalSeparator = ",";
nfi.NumberGroupSeparator = " ";
string s1 = new string(n1.ToString("N", nfi).Reverse().SkipWhile(x => x == '0' || x == ',').Reverse().ToArray());
string s2 = new string(n2.ToString("N", nfi).Reverse().SkipWhile(x => x == '0' || x == ',').Reverse().ToArray());
string s3 = new string(n3.ToString("N", nfi).Reverse().SkipWhile(x => x == '0' || x == ',').Reverse().ToArray());
string s4 = new string(n4.ToString("N", nfi).Reverse().SkipWhile(x => x == '0' || x == ',').Reverse().ToArray());

原因是以下两件事:

  1. 首先,double而不是精确的。当您输入类似double n = 1234.5的内容时,实际存储的double值可能类似于n = 1234.499999999999998

  2. 其次是关于ToString()。它实际上只用于格式化。换句话说,取决于你如何支配它,它会显示出一些东西。例如,如果您给出的指令显示小数分隔符后有2个有效数字的数字,那么它将显示小数分隔符号后正好有2个无效数字的数字。

现在,把这两件事放在一起,我们在这里陷入了困境!你想让程序做的是:"显示我需要的有效位数"。但是,当您输入double n = 1234.5时,程序将显示1234.499999999999998!但另一方面,您也不希望固定小数分隔符后的数字。

所以,我想您应该使用LINQ SkipWhileReverse来实现这一点,而不是简单的内置。

//这是您的最佳方式:

s3=n3.ToString($"####0{CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator}00");

//或者我更喜欢:

s3=n3.ToString($"###{CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator}#0{CultureInfo.CCurrentCulture.NNumberFormat.NumberDecimalSeparator}00");

//我一直在用这个。没有。这是最好和最小的方式。:)//谨致问候。Ersin Kecis。

您可以增加NumberFormatInfo中的小数位数,并使用TrimEnd()删除额外的零和分隔符。

    nfi.NumberDecimalDigits = 15;
    public static string FormatDouble(double d, NumberFormatInfo nfi)
    {
        return d.ToString("N", nfi).TrimEnd('0', ' ', ',', '.');
    }

最新更新