提问人:Nick 提问时间:1/29/2022 最后编辑:EnigmativityNick 更新时间:1/29/2022 访问量:258
C# 十进制(字符串类型)在最后一个字符处舍入
C# decimal (type string) rounded at the last char
问:
这似乎是重复的,但我找不到正确的答案(问题足够接近,但是..) 我有一个字符串,它代表一个十进制数,它总是有很多小数位,至少 20 位,有时最多 2000 位(代表特定的验证计算,即像“数字 135 到 147 是质数 X 等 - 只是为了给你一些上下文)
例如:
123.829743892473218762329384241002373824970132871283923423961723816273823447623528347662123874999
我正在尝试四舍五入到倒数第二位。我建立了一个(有点)有效的小方法。但是(如上例所示)..如果最后一位数字是 >4,倒数第二位是 9,这意味着我必须提高前一位数字,并削减最后一位数字,如果前一位数字也是 9,这意味着我也必须提高前一位数字,依此类推。
前任。
123.99999 // must become 124
123.823467283762378 // must become 123.82346728376238
123.823467283762398 // must become 123.8234672837624 (notice the last 0 has gone)
123.09999999 // must become 123.1 (notice no trailing zeros needed..)
122.00000009 // must become 122.0000001
124.81379281 // must become simply 124.8137928
129.07872345 // must become 129.0787235
129.07872344 // must become 129.0787234
依此类推。换句话说,它只是通过仅切割最后一位数字来对数字(这是一个字符串!!)进行四舍五入,但继续向左四舍五入,直到不需要为止。 只有最后一位小数才需要舍入(如果数字是整数,则不需要),规则是,如果最后一位的数字是 >4,则数字被剪切,前一位(左边)数字被提高 1,忽略尾随的零(如果有的话),规则一直持续到整数部分的最后一位(即 123.99999 变为 124, 但整数 124 保持原样等)。
有人可以帮我为此构建一个字符串扩展吗?
using System;
public class Example
{
public string round(string LargeDecimal)
{
Console.WriteLine("Number as string is: " + LargeDecimal);
int lastDigit = (int)char.GetNumericValue(LargeDecimal[LargeDecimal.Length -1]); // get last character
Console.WriteLine("lastDigit = " + lastDigit.ToString());
string number = LargeDecimal.Remove(LargeDecimal.Length - 1); // delete last character
Console.WriteLine("Now number is " + number);
if (lastDigit > 4)
{
Console.WriteLine("Last digit {0} was >4", lastDigit.ToString());
int newLastDigit = (int)char.GetNumericValue(number[number.Length -1]);
Console.WriteLine("Next to left last digit is {0} which will be raised by 1 and become {1}", newLastDigit.ToString(), (newLastDigit +1).ToString());
newLastDigit += 1; //increase by one
number = number.Remove(number.Length - 1); // delete ex-penultimate (and now last) character
number = number + newLastDigit.ToString();
return number; // and add a digit increased by 1
}
else
{
return LargeDecimal.Remove(LargeDecimal.Length - 2);
}
}
public Example()
{}
}
public class Program
{
public static void Main(string[] args)
{
string myNumber = "124.2398478278268985738276523548769";
Example myExample = new Example();
string result = myExample.round(myNumber);
Console.WriteLine("Now I have " + result);
}
}
答:
这是一个解决方案:
public static decimal RoundLastChar(this string input)
{
decimal inputDecimal = Convert.ToDecimal(input, new CultureInfo("en-US"));
int decimalPlaces = BitConverter.GetBytes(decimal.GetBits(inputDecimal)[3])[2];
if (decimalPlaces == 0) return inputDecimal;
decimal result = Math.Round(inputDecimal, decimalPlaces - 1, MidpointRounding.AwayFromZero).Normalize();
return result;
}
public static decimal Normalize(this decimal value)
{
return value / 1.000000000000000000000000000000000m;
}
评论
因为您的输入值是字符串类型,所以我会用它来确保输入是有效的数字。decimal.TryParse
decimal
然后,您可以尝试使用一个简单的算法从输入中计算浮点长度,然后执行Math.Round
static decimal RoundFirstSignificantDigit(string input) {
decimal significantDigit;
if (!decimal.TryParse(input,out significantDigit))
{
throw new ArgumentException("Invalid input!!");
}
var floatLength = input.Split('.')[1].Length;
return Math.Round(significantDigit, floatLength - 1, MidpointRounding.AwayFromZero);
}
编辑
如果你的输入有大小数,由于C#十进制只允许&之间的范围,目前不支持像Java那样。±1.0 × 10-28 to ±7.9228 × 1028
BigDecimal
我认为有两种方法可以做到这一点。
- 制作自己的圆形逻辑。
BigDecimal
- 您可以尝试使用支持您从 java 调用的 IKVM 库。
BigDecimal
您可以简单地通过IKVM实现您的期望。
static string RoundFirstSignificantDigit(string input) {
BigDecimal significantDigit = new BigDecimal(input);
var floatLength = input.Split('.')[1].Length;
return significantDigit.setScale(floatLength - 1, BigDecimal.ROUND_HALF_UP).toString().TrimEnd('0');
}
评论
嗯,这很棘手,因为小数点后 2000 位你不能使用 .所以也许有比这更简单的东西,但它可能对你有用(.NET 小提琴演示):decimal
public static string RoundLongNumber(this string input)
{
if (!ValidLongNumber(input, out bool isInteger) || isInteger) return input;
int index = input.IndexOf('.');
string part1 = input.Remove(index);
string part2 = input.Substring(index + 1);
StringBuilder sb = new StringBuilder(part2);
while(true)
{
if (LastInt() <= 4)
{
return BuildNumber();
}
sb.Length = sb.Length - 1; // remove last
int lastInt = LastInt() + 1;
while (lastInt == 10)
{
sb.Length = sb.Length - 1;
if (sb.Length == 0)
{
// just integer remaining
int num = int.Parse(part1);
return (++num).ToString();
}
lastInt = LastInt() + 1;
}
sb[sb.Length - 1] = (char)(lastInt + '0');
if (lastInt != 9) return BuildNumber();
}
int LastInt() => sb[sb.Length - 1] - '0';
string BuildNumber() => part1 + "." + sb.ToString();
}
private static bool ValidLongNumber(string number, out bool isInteger)
{
isInteger = true;
if (string.IsNullOrWhiteSpace(number)) return false;
int pointCount = 0;
foreach(char c in number)
{
bool isDigit = char.IsDigit(c);
bool isPoint = c == '.';
if (!isDigit) isInteger = false;
if (isPoint) pointCount++;
if(pointCount > 1 || (!isPoint && !isDigit)) return false;
}
return true;
}
下面是你的示例:
public static void Main()
{
var strings = new List<string> {
"123.99999", // must become 124
"129.99999", // must become 130
"123.823467283762378", // must become 123.82346728376238
"123.823467283762398", // must become 123.8234672837624 (notice the last 0 has gone)
"123.09999999", // must become 123.1 (notice no trailing zeros needed..)
"122.00000009", // must become 122.0000001
"124.81379281", // must become simply 124.8137928 >>> Why? Should remain same
"129.07872345", // must become 129.0787235
"129.07872344", // must become 129.0787234 >>> Why? Should remain same
};
IEnumerable<string> results = strings.Select(s => s.RoundLongNumber());
foreach(var res in results)
{
Console.WriteLine(res);
}
}
请注意,两个结果是不同的,但要么你没有解释该规则,要么你的预期是错误的:
124.81379281 // must become simply 124.8137928
129.07872344 // must become 129.0787234
为什么?根据我的理解,两者应该保持不变。
评论
上一个:C# 为结构错误的属性赋值
评论