Math.Round满足不了我的要求,需要一个自适应的最优解,
Test Case的情况还有很多,示例只是简单列了一些,注释是需要返回的结果
完成GetNiceNumber方法,测试通过,符合要求就能得分.
static void Main(string[] args)
{
var doubleArray = new double[]
{
2.23339923,//2.2334
4.99234229999,//4.9923423
4.00230000499,//4.0023
2.3299230000239999,//2.339923
3.33990009,//3.34
5.999923,//6
0.9992332,//1
0.992000099923//0.992
}; foreach (var val in doubleArray)
{
Console.WriteLine("original = {0}, nice = {1}", val, GetNiceNumber(val));
}
Console.Read();
} static double GetNiceNumber(double input)
{
//todo...
}
Test Case的情况还有很多,示例只是简单列了一些,注释是需要返回的结果
完成GetNiceNumber方法,测试通过,符合要求就能得分.
static void Main(string[] args)
{
var doubleArray = new double[]
{
2.23339923,//2.2334
4.99234229999,//4.9923423
4.00230000499,//4.0023
2.3299230000239999,//2.339923
3.33990009,//3.34
5.999923,//6
0.9992332,//1
0.992000099923//0.992
}; foreach (var val in doubleArray)
{
Console.WriteLine("original = {0}, nice = {1}", val, GetNiceNumber(val));
}
Console.Read();
} static double GetNiceNumber(double input)
{
//todo...
}
一个浮点类型,在做dB变换(log10)和逆变换(power10)之后精度会发生变化,但这个变化是微乎其微的.
比如
3.3 变成 3.299999
2.0 变成 2.0000883
工程上实际可以忽略这样的变化,如何忽略,就需要一个好的Nice number算法.
Console.WriteLine(Math.Round(4.99234229999, 7, MidpointRounding.AwayFromZero).ToString());
Console.WriteLine(Math.Round(4.00230000499, 4, MidpointRounding.AwayFromZero).ToString());
Console.WriteLine(Math.Round(2.3299230000239999, 6, MidpointRounding.AwayFromZero).ToString());
Console.WriteLine(Math.Round(3.33990009, 2, MidpointRounding.AwayFromZero).ToString());
Console.WriteLine(Math.Round(5.999923, 0, MidpointRounding.AwayFromZero).ToString());
Console.WriteLine(Math.Round(0.9992332, 0, MidpointRounding.AwayFromZero).ToString());
Console.WriteLine(Math.Round(0.992000099923, 3, MidpointRounding.AwayFromZero).ToString());
// 你的例子是有问题的:2.23339923, // 2.2334
4.99234229999, // 4.9923423
4.00230000499, // 4.0023
2.3299230000239999, // 2.339923// 看到这里,我得出的结论是:
// 当遇到第一个重复次数最多的数字串时,截取该子串之前的数字值,并四舍五入!3.33990009, //3.34// 当我看到这里的时候,我看不懂了,以上推论不成立了,
// 因为您没有截取 '000' 这个最长子串之前的数字,
// 而是截取了 '99' 之前的数字!// 但是,之前的 2.3299230000... 这个例子,您却没有截取 '99' 之前的数字,
// 这是怎么回事,楼主,Demo 不能乱写!
既然要求自适应,为什么要传小数进度呢?
我一开始就说了Round满足不了实际需求.
你有心了,我引用的时候错了,我想用的是
3.33990009, //3.34
这个做例子所有Test Case都是说的通的.不会胡乱给.
static double GetNiceNumber(double input)
{
string[] str = input.ToString().Split('.'); //最后一位可四舍五入的位置
int holdIndex = 0; //第一个大于零的数索引
char num1 = str[1].First(p => int.Parse(p.ToString()) > 0);
int startIndex = str[1].IndexOf(num1); //是否含零的情况,并得到第一个零的位置
int zeroIndex = str[1].IndexOf('0', startIndex);
if (zeroIndex > 0)
{
str[1] = str[1].Remove(zeroIndex);
if (int.Parse(str[1].Last().ToString()) >= 5)
{
holdIndex = str[1].Length - 1;
}
else
{
holdIndex = str[1].Length;
}
}
else
{
if (str[1].Any(p => int.Parse(p.ToString()) >= 5))
{
char lastNumber = str[1].Last(p => int.Parse(p.ToString()) >= 5);
holdIndex = str[1].LastIndexOf(lastNumber);
}
else
{
holdIndex = str[1].Length;
}
}
return Math.Round(input, holdIndex);
}献丑了,可以试一下!
{
double[] d = new double[]
{
2.23339923,//2.2334
4.99234229999,//4.9923423
4.00230000499,//4.0023
2.3299230000239999,//2.339923
3.33990009,//3.34
5.999923,//6
0.9992332,//1
0.992000099923,//0.992
0.200999, //0.201
0.2009000099, //0.201
0.2002540099, //0.200254
0.200499009 //0.201
};
for (int i = 0; i < d.Length; i++)
{
Console.WriteLine("你转换的数:" + d[i] + "转换后数是:" + GetNiceNumber(d[i]));
}
Console.ReadLine();
}
static double GetNiceNumber(double input)
{
string[] str = input.ToString().Split('.');
double newNumber = input; List<int> list = new List<int>();
for (int i = 0; i < str[1].Length; i++)
{
int temp = int.Parse(str[1][i].ToString());
list.Add(temp);
}
int index = list.Count - 1;
int firstZero = list.IndexOf(0);
int lastZero = list.LastIndexOf(0);
while (true)
{
if (list.Contains(0))
{
if (list[index] >= 5)
{
index--;
list[index]++;
continue;
}
else if (list[index] < 5 && list[index] > 0 && index > lastZero)
{
index--;
continue;
}
else if (list[index] < 5 && list[index] > 0 && index < lastZero)
{ string d = str[0] + '.' + string.Join("", list.Take(index + 1));
newNumber = Convert.ToDouble(d);
break; }
else if (list[index] < 5 && list[index] > 0 && index == lastZero)
{
if (list.Skip(firstZero).Take(index).Any(p => p >= 5))
{
index--;
continue;
}
else
{
newNumber = Math.Round(newNumber, index + 1);
break;
}
}
else if (list.Skip(firstZero).Take(index).Any(p => p >= 5))
{
if (list[index] >= 5)
{
index--;
list[index]++;
}
else
{
index--;
}
continue;
}
else
{
newNumber = Math.Round(newNumber, index);
break;
}
}
else
{
if (list[index] >= 5)
{
newNumber = Math.Round(newNumber, index);
break;
}
else
{
index--;
}
}
}
return newNumber;
}
只需要关心00情况的舍弃和99情况的进位,不需要做0.04999进位成0.1的情况
你的Test Case: 0.200499009 //0.201 不能满足我的要求
其实我一开始就提了Round是满足不了要求的, 请继续努力,快接近目标了.
static double GetNiceNumber(double input)
{
char[] numbers = input.ToString().ToCharArray();
int length = numbers.Length - 1, maxCount = 1, dotIndex = length;
char lastNumber = numbers[length], number;
for (int count = 0, index = length - 1; index >= 0; --index)
{
if ((number = numbers[index]) == '.') dotIndex = index;
else
{
if (number == lastNumber) ++count;
else
{
if (count >= maxCount && ((lastNumber -= '0') == 0 || lastNumber == 9))
{
if (lastNumber != 0) numbers[index] = (char)(number + 1);
length = index;
if (--count > maxCount) maxCount = count;
}
count = 0;
lastNumber = number;
}
}
}
while (++length < dotIndex) numbers[length] = '0';
return double.Parse(new string(numbers, 0, length));
}
static double GetNiceNumber(double input)
{
char[] numbers = input.ToString().ToCharArray();
int dotIndex = numbers.Length, length = dotIndex - 1, maxCount = 1;
char lastNumber = numbers[length], number;
for (int count = 0, index = length - 1; index >= 0; --index)
{
if ((number = numbers[index]) == '.') dotIndex = index;
else
{
if (number == lastNumber) ++count;
else
{
if (count >= maxCount && ((lastNumber -= '0') == 0 || lastNumber == 9))
{
if (lastNumber != 0) numbers[index] = (char)(number + 1);
length = index;
if (--count > maxCount) maxCount = count;
}
count = 0;
lastNumber = number;
}
}
}
while (++length < dotIndex) numbers[length] = '0';
return double.Parse(new string(numbers, 0, length));
}
4.002300004999993,nice number 是不是 4.002300005?
{
double[] doubleArray = new double[]
{
2.23339923,//2.2334
4.99234229999,//4.9923423
4.00230000499,//4.0023
2.3299230000239999,//2.339923
3.33990009,//3.34
5.999923,//6
0.9992332,//1
0.992000099923,//0.992
0.200999 //0.201
}; foreach (double val in doubleArray)
{
Console.WriteLine("original = {0}, nice = {1}", val, GetNiceNumber(val));
}
} private double GetNiceNumber(double input)
{
String s = input.ToString();
int n = 0;
double temp = 1; if ((s.LastIndexOf("99") >= 1))
{
s = s.Substring(0,s.LastIndexOf("99"));
input = Convert.ToDouble(s);
n = s.Substring(s.IndexOf(".") + 1).Length;
for (int i = 1; i <= n; i++)
{
temp *= 0.1;
}
input += temp;
} s = input.ToString();
if ((s.LastIndexOf("00") >= 1))
{
s = s.Substring(0, s.LastIndexOf("00"));
input = Convert.ToDouble(s);
} return input;
}//输出
//original = 2.23339923, nice = 2.2334
//original = 4.99234229999, nice = 4.9923423
//original = 4.00230000499, nice = 4.0023
//original = 2.329923000024, nice = 2.33
//original = 3.33990009, nice = 3.34
//original = 5.999923, nice = 6
//original = 0.9992332, nice = 1
//original = 0.992000099923, nice = 0.992
//original = 0.200999, nice = 0.201
original = 0.200999, nice = 0.201如果是先去99再去00的话,或者先去00再去99;那么这两值又与LZ需求不符,矛盾中
所以我觉得应该由LZ先明确取值规则
恩,我想让楼主回答的问题也是帮助分析得出规则。感觉这个规则只和 0、9 有关,但是不确定,
楼主给出的 demo 太少了,而且都有 0、9,如果是 5.22334455 呢?又或者 0、9 多大子串长度一样呢?
private void button1_Click(object sender, EventArgs e)
{
//string str = "android:versionCode=\"第一版\"";
//int begin = str.IndexOf("=");
//string str1 = str.Substring(0, 21);
//string s = str.Remove(0, begin+1);
//MessageBox.Show(s); double[] doubleArray = new double[]
{
2.23339923,//2.2334
4.99234229999,//4.9923423
4.00230000499,//4.0023
2.3299230000239999,//2.339923
3.33990009,//3.34
5.999923,//6
0.9992332,//1
0.992000099923,//0.992
0.200999 //0.201
};
string str="";
foreach (double val in doubleArray)
{
str+="original = "+val+", nice ="+ GetNiceNumber(val)+"\n";
}
MessageBox.Show(str);
} private double GetNiceNumber(double input)
{
String str = input.ToString();
int n = 0;
double temp = 1; if ((str.LastIndexOf("9") >= 1))
{
str = str.Substring(0, str.LastIndexOf("9"));
input = Convert.ToDouble(str);
n = str.Substring(str.IndexOf(".") + 1).Length;//小数点
for (int i = 1; i <= n; i++)
{
temp *= 0.1;
}
input += temp;
} str = input.ToString();
if ((str.LastIndexOf("0") >= 1))
{
str = str.Substring(0, str.LastIndexOf("0"));
input = Convert.ToDouble(str);
} return input;
}
static double GetNiceNumber(double input)
{
char[] numbers = input.ToString().ToCharArray();
int dotIndex = 0, length = numbers.Length, maxCount = 1, addValue = 0;
while (dotIndex != length && numbers[dotIndex] != '.') ++dotIndex;
if (dotIndex != length--)
{
char lastNumber = numbers[length], number;
for (int count = 0, index = length - 1; index >= 0; --index)
{
if ((number = numbers[index]) != '.')
{
if ((number = numbers[index]) == lastNumber) ++count;
else
{
if ((lastNumber -= '0') == 0)
{
int endCount = numbers[index + count + 1] == '0' ? 0 : 1;
if ((count -= endCount) >= maxCount)
{
if (index < dotIndex)
{
length = dotIndex - 1;
break;
}
else length = index;
if ((count -= (endCount ^= 1)) > maxCount) maxCount = count;
}
}
else if (lastNumber == 9)
{
if (count >= maxCount)
{
if (index < dotIndex)
{
addValue = 1;
length = dotIndex - 1;
break;
}
else length = index;
numbers[index] = (char)(number + 1);
if (--count > maxCount) maxCount = count;
}
}
count = 0;
lastNumber = number;
}
}
}
}
return double.Parse(new string(numbers, 0, ++length)) + addValue;
}
看下方法重载列表
Math.Round (Decimal) 将小数值舍入到最接近的整数。
由 .NET Compact Framework 支持。
Math.Round (Double) 将双精度浮点值舍入为最接近的整数。
由 .NET Compact Framework 支持。
Math.Round (Decimal, Int32) 将小数值舍入到指定精度。
由 .NET Compact Framework 支持。
Math.Round (Decimal, MidpointRounding) 将小数值舍入到最接近的整数。一个参数,指定当一个值正好处于另两个数中间时如何舍入这个值。
Math.Round (Double, Int32) 将双精度浮点值舍入到指定精度。
由 .NET Compact Framework 支持。
Math.Round (Double, MidpointRounding) 将双精度浮点值舍入为最接近的整数。一个参数,指定当一个值正好处于另两个数中间时如何舍入这个值。
Math.Round (Decimal, Int32, MidpointRounding) 将小数值舍入到指定精度。一个参数,指定当一个值正好处于另两个数中间时如何舍入这个值。
Math.Round (Double, Int32, MidpointRounding) 将双精度浮点值舍入到指定精度。一个参数,指定当一个值正好处于另两个数中间时如何舍入这个值。 http://msdn.microsoft.com/zh-cn/library/system.math.round(v=vs.80).aspx
// 来,请楼主看看我这个对不对。
// 你给的那些测试数据我测下来没有问题。public static double GetNiceNumber(double number)
{
return GetNiceNumber(number.ToString());
}
private static double GetNiceNumber(string number)
{
string fraction = GetFraction(number);
if (String.IsNullOrWhiteSpace(fraction))
{
return Double.Parse(number);
} var maxSubstrInfo = GetMaxSubstringInfo(fraction);
if (maxSubstrInfo.Value < 0)
{
return Double.Parse(number);
}
if (maxSubstrInfo.Value == 0)
{
return Math.Round(Double.Parse(number));
} bool carry = false;
string integer = GetInteger(number);
string fractionCutted = fraction.Substring(0, maxSubstrInfo.Value); if (maxSubstrInfo.Key == "9")
{
fractionCutted = Increment(fractionCutted);
}
if (fractionCutted[fractionCutted.Length - 1] == '9')
{
fractionCutted = Increment(fractionCutted, out carry);
} if (carry)
{
integer = Increment(integer);
}
string newNumber = String.Format("{0}.{1}", integer, fractionCutted); return Double.Parse(newNumber);
}
private static string Increment(string number)
{
bool carry = false;
number = Increment(number, out carry);
if (carry)
{
return number.Insert(0, "1");
}
return number;
}
private static string Increment(string number, out bool carry)
{
carry = false;
var digits = number.ToList();
for (int i = digits.Count - 1; i >= 0; i--)
{
int value = ((digits[i] - 48) + 1) % 10;
digits[i] = (char)(value + 48); if (value != 0)
{
return String.Join<char>(String.Empty, digits);
}
}
carry = true; return String.Join<char>(String.Empty, digits);
}
private static KeyValuePair<string, int> GetMaxSubstringInfo(string number)
{
// 至少重复 2 次
var matches = Regex.Matches(number, @"(?<ch>[09])\1+").Cast<Match>();
if (matches.Count() == 0)
{
return new KeyValuePair<string, int>(String.Empty, -1);
}
var maxLength = matches.Max(m => m.Length);
if (maxLength == 1)
{
return new KeyValuePair<string, int>(number[0].ToString(), -1);
}
var match = matches.Where(m => m.Length == maxLength).ElementAt(0); // <index, max length>
return new KeyValuePair<string, int>(match.Groups["ch"].Value, match.Index);
}
private static string GetInteger(string number)
{
return Regex.Match(number, @"^(?<integer>\d+)(\.\d+)?$").Groups["integer"].Value;
}
private static string GetFraction(string number)
{
return Regex.Match(number, @"^\d+\.(?<fraction>\d+)$").Groups["fraction"].Value;
}
{ Int64 accuracy = 1000;//精度定义,必须是10的倍数
Int64 i = 1;//四舍五入精度 String inputStr = input.ToString();
Int64 maxi = (Int64)Math.Pow(10, inputStr.Length - inputStr.IndexOf(".") - Math.Log10(accuracy));//最大精度
Double round = 0;//四舍五入临时值
Double curDeviation = 0;//当前的误差
Double minDeviation = accuracy;//最小误差
Double lasti = i; //最优改的四舍五入精度
do
{
round = Math.Round(input * i);
curDeviation = Math.Round(Math.Abs((round - input * i) * accuracy) % accuracy);
if (minDeviation > curDeviation)
{
minDeviation = curDeviation;
lasti = i;
}
i *= 10;
} while (i < maxi);
return Math.Round(input * lasti) / lasti;
}
这个无法解决
3.33990009,//3.34
上面程序转出来的答案是3.3399
{ Int64 accuracy = 1000;//精度定义,必须是10的倍数
Int64 i = 1;//四舍五入精度 String inputStr = input.ToString();
Int64 maxi = (Int64)Math.Pow(10, inputStr.Length - inputStr.IndexOf(".") - Math.Log10(accuracy));//最大精度
Double round = 0;//四舍五入临时值
Double curDeviation = 0;//当前的误差
Double minDeviation = accuracy;//最小误差
Double lasti = i; //最优改的四舍五入精度
do
{
round = Math.Round(input * i);
curDeviation = Math.Round(Math.Abs((round - input * i) * accuracy) % accuracy);
if (minDeviation > curDeviation)
{
minDeviation = curDeviation;
lasti = i;
}
i *= 10;
} while (i < maxi);
if (minDeviation == 1)
{
lasti /= 10;
}
return Math.Round(input * lasti) / lasti;
}已修正3.33990009,//3.34的问题
{ Int64 accuracy = 1000;//精度定义,必须是10的倍数
Int64 i = 1;//四舍五入精度 String inputStr = input.ToString();
Int64 maxi = (Int64)Math.Pow(10, inputStr.Length - inputStr.IndexOf(".") - Math.Log10(accuracy));//最大精度
Double round = 0;//四舍五入临时值
Double curDeviation = 0;//当前的误差
Double minDeviation = accuracy;//最小误差
Double lasti = i; //最优改的四舍五入精度
do
{
round = Math.Round(input * i);
curDeviation = Math.Round(Math.Abs((round - input * i) * accuracy) % accuracy);
if (minDeviation > curDeviation)
{
minDeviation = curDeviation;
lasti = i;
}
i *= 10;
} while (i < maxi);
if (minDeviation == 1 && lasti > 1)
{
lasti /= 10;
}
return Math.Round(input * lasti) / lasti;
}再次更正……-_-!!
这次不会有错了
0.200999 => 0.201 你的结果是0.2
4.00230000499999 => 4.002300005 你的结果是4.0023
1. 如果 LS 是 9,则截取后的小数位 fraction 进位;
2. 如果 LS 是 0,如果截取后的小数位 fraction 最后一位是 9,fraction 进一位。如果是一直是 9,那就一直进,进到不能再进位置。极端情况:9999999.999000000000234 => 10000000
{
Int64 accuracy = 10;//动态精度定义
Int64 i;//四舍五入精度
String inputStr = input.ToString();
Double round = 0;//四舍五入临时值
Double curDeviation = 0;//当前的误差
Double minDeviation = Double.MaxValue;//最小误差
Double lasti = 1; //最优解的四舍五入精度
Int64 maxi = 0;//最大精度
while (true)
{
maxi = (Int64)Math.Pow(10, inputStr.Length - inputStr.IndexOf(".") - Math.Log10(accuracy));
i = 1;
if (maxi <= i)
{
break;
}
while (i < maxi)
{
round = Math.Round(input * i);
curDeviation = Math.Round(Math.Abs((round - input * i) * accuracy) % accuracy) + 1;
if (minDeviation > curDeviation / accuracy)
{
minDeviation = curDeviation / accuracy;
lasti = i;
}
i *= 10;
}
accuracy *= 10;
}
Double result = Math.Round(input * lasti) / lasti;
if (result.ToString().EndsWith("99"))
{
lasti /= 10;
result = Math.Round(input * lasti) / lasti;
}
return result;
}现在精度定义也是动态计算的,已知数据测试通过,如果还有问题的话,请楼主提供多些数据以供测试。
0.100099899 => 0.1
0.100019999 => 0.10002
0.998999...9 => 0.999
0.99000...01 => 1
0.100019999 => 0.10002这个不对,我觉得应该是 0.1,你可以参考楼主的 demo
2.3299230000239999, //2.339923
sorry,看错了,你举的例子没错 :)
我说的极端情况是 进位 的极端情况。