使用不寻常的规则格式化十进制值
Format decimal value with unusual rules
根据以下规则将十进制值转换为字符串的优雅方法是什么?
- 显示小数点前的所有数字。
- 始终显示逗号代替小数点。
- 如果小数点后部分不为零,则只显示有效数字,但最少为2。
示例:
decimal string
------------ ----------
500000 500000,
500000.9 500000,90
500000.90 500000,90
500000.900 500000,90
500000.9000 500000,90
500000.99 500000,99
500000.999 500000,999
500000.9999 500000,9999
通过将值转换为 int
,我可以轻松显示小数点前的部分和逗号。但是处理小数点后部分的不同情况变得冗长乏味。
如果有一种方法可以指定我只需要小数点后的数字,而不需要小数点,那么我手边就有了。比如String.Format("{0:.00#}", value)
,只是不显示小数点。
我不会称之为漂亮,但它属于 "it works" 的类别。
首先是实施,
public static class FormatProviderExtensions
{
public static IFormatProvider GetCustomFormatter(this NumberFormatInfo info, decimal d)
{
var truncated = Decimal.Truncate(d);
if (truncated == d)
{
return new NumberFormatInfo
{
NumberDecimalDigits = 0,
NumberDecimalSeparator = info.NumberDecimalSeparator,
NumberGroupSeparator = info.NumberGroupSeparator
};
}
// The 4th element contains the exponent of 10 used by decimal's
// representation - for more information see
// https://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx
var fractionalDigitsCount = BitConverter.GetBytes(Decimal.GetBits(d)[3])[2];
return fractionalDigitsCount <= 2
? new NumberFormatInfo
{
NumberDecimalDigits = 2,
NumberDecimalSeparator = info.NumberDecimalSeparator,
NumberGroupSeparator = info.NumberGroupSeparator
}
: new NumberFormatInfo
{
NumberDecimalDigits = fractionalDigitsCount,
NumberDecimalSeparator = info.NumberDecimalSeparator,
NumberGroupSeparator = info.NumberGroupSeparator
};
}
}
和用法示例:
var d = new[] { 500000m, 500000.9m, 500000.99m, 500000.999m, 500000.9999m };
var info = new NumberFormatInfo { NumberDecimalSeparator = ",", NumberGroupSeparator = "" };
d.ToList().ForEach(x =>
{
Console.WriteLine(String.Format(info.GetCustomFormatter(x), "{0:N}", x));
});
输出:
500000
500000,90
500000,99
500000,999
500000,9999
它从现有的 NumberFormatInfo
和 returns 具有我们想要的 NumberDecimalDigits
的新属性中获取我们关心的属性。它在丑陋的规模上相当高,但使用起来很简单。
这里有一个简洁明了的解决方案(.NET Fiddle):
public static string FormatDecimal(decimal d)
{
string s = d.ToString("0.00##", NumberFormatInfo.InvariantInfo).Replace(".", ",");
if (s.EndsWith(",00", StringComparison.Ordinal))
s = s.Substring(0, s.Length - 2); // chop off the "00" after integral values
return s;
}
如果您的值可能包含四个以上的小数位,请根据需要添加额外的 #
个字符。具有 28 个小数位的格式字符串 0.00##########################
将容纳所有可能的 decimal
值。
不知道您希望这有多优雅,但这是实现您要求的直接方法。
List<decimal> decimals = new List<decimal>
{
500000M,
500000.9M,
500000.99M,
500000.999M,
500000.9999M,
500000.9000M
};
foreach (decimal d in decimals)
{
string dStr = d.ToString();
if (!dStr.Contains("."))
{
Console.WriteLine(d + ",");
}
else
{
// Trim any trailing zeroes after the decimal point
dStr = dStr.TrimEnd('0');
string[] pieces = dStr.Split('.');
if (pieces[1].Length < 2)
{
// Ensure 2 significant digits
pieces[1] = pieces[1].PadRight(2, '0');
}
Console.WriteLine(String.Join(",", pieces));
}
}
结果:
500000,
500000,90
500000,99
500000,999
500000,9999
500000,90
根据以下规则将十进制值转换为字符串的优雅方法是什么?
- 显示小数点前的所有数字。
- 始终显示逗号代替小数点。
- 如果小数点后部分不为零,则只显示有效数字,但最少为2。
示例:
decimal string
------------ ----------
500000 500000,
500000.9 500000,90
500000.90 500000,90
500000.900 500000,90
500000.9000 500000,90
500000.99 500000,99
500000.999 500000,999
500000.9999 500000,9999
通过将值转换为 int
,我可以轻松显示小数点前的部分和逗号。但是处理小数点后部分的不同情况变得冗长乏味。
如果有一种方法可以指定我只需要小数点后的数字,而不需要小数点,那么我手边就有了。比如String.Format("{0:.00#}", value)
,只是不显示小数点。
我不会称之为漂亮,但它属于 "it works" 的类别。
首先是实施,
public static class FormatProviderExtensions
{
public static IFormatProvider GetCustomFormatter(this NumberFormatInfo info, decimal d)
{
var truncated = Decimal.Truncate(d);
if (truncated == d)
{
return new NumberFormatInfo
{
NumberDecimalDigits = 0,
NumberDecimalSeparator = info.NumberDecimalSeparator,
NumberGroupSeparator = info.NumberGroupSeparator
};
}
// The 4th element contains the exponent of 10 used by decimal's
// representation - for more information see
// https://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx
var fractionalDigitsCount = BitConverter.GetBytes(Decimal.GetBits(d)[3])[2];
return fractionalDigitsCount <= 2
? new NumberFormatInfo
{
NumberDecimalDigits = 2,
NumberDecimalSeparator = info.NumberDecimalSeparator,
NumberGroupSeparator = info.NumberGroupSeparator
}
: new NumberFormatInfo
{
NumberDecimalDigits = fractionalDigitsCount,
NumberDecimalSeparator = info.NumberDecimalSeparator,
NumberGroupSeparator = info.NumberGroupSeparator
};
}
}
和用法示例:
var d = new[] { 500000m, 500000.9m, 500000.99m, 500000.999m, 500000.9999m };
var info = new NumberFormatInfo { NumberDecimalSeparator = ",", NumberGroupSeparator = "" };
d.ToList().ForEach(x =>
{
Console.WriteLine(String.Format(info.GetCustomFormatter(x), "{0:N}", x));
});
输出:
500000
500000,90
500000,99
500000,999
500000,9999
它从现有的 NumberFormatInfo
和 returns 具有我们想要的 NumberDecimalDigits
的新属性中获取我们关心的属性。它在丑陋的规模上相当高,但使用起来很简单。
这里有一个简洁明了的解决方案(.NET Fiddle):
public static string FormatDecimal(decimal d)
{
string s = d.ToString("0.00##", NumberFormatInfo.InvariantInfo).Replace(".", ",");
if (s.EndsWith(",00", StringComparison.Ordinal))
s = s.Substring(0, s.Length - 2); // chop off the "00" after integral values
return s;
}
如果您的值可能包含四个以上的小数位,请根据需要添加额外的 #
个字符。具有 28 个小数位的格式字符串 0.00##########################
将容纳所有可能的 decimal
值。
不知道您希望这有多优雅,但这是实现您要求的直接方法。
List<decimal> decimals = new List<decimal>
{
500000M,
500000.9M,
500000.99M,
500000.999M,
500000.9999M,
500000.9000M
};
foreach (decimal d in decimals)
{
string dStr = d.ToString();
if (!dStr.Contains("."))
{
Console.WriteLine(d + ",");
}
else
{
// Trim any trailing zeroes after the decimal point
dStr = dStr.TrimEnd('0');
string[] pieces = dStr.Split('.');
if (pieces[1].Length < 2)
{
// Ensure 2 significant digits
pieces[1] = pieces[1].PadRight(2, '0');
}
Console.WriteLine(String.Join(",", pieces));
}
}
结果:
500000,
500000,90
500000,99
500000,999
500000,9999
500000,90