小数转分数的简便方法






3.89/5 (9投票s)
如何在 C# 中将给定的十进制数或双精度数转换为分数
引言
将小数简化为分数在编写解决数学问题的程序时非常有用。用分数表示长小数和循环小数比它们的小数形式更清晰。
背景
在研究了确定分数的小数算法后,大部分内容来自一篇 1991 年发表的论文,我觉得它有点复杂,并且在 stackoverflow 上提问后,我决定编写一个更简单的代码,以一种清晰的方式完成相同的任务。但是,此代码有一些缺点,因为它主要侧重于使用 string
操作(一种更自然的方式)来实现其目的。
Using the Code
该代码非常易于使用。有一个名为 dec2frac
的方法,它接受一个 double
参数。调用此方法会返回简化小数的等效分数的 string
表示形式。
它是如何工作的:
- 确定给定的十进制数是否为负数。
- 将十进制数转换为绝对值。
- 获取给定小数的整数部分。
- 获取小数部分。
- 检查小数是否为循环小数。如果小数是循环小数,则返回精确的循环小数。
- 如果小数不是循环小数,则通过将分子更改为 10^小数位数来开始简化,否则从分子中减去 1。
- 然后简化分数。
让我们看一下 dec2frac
方法
private static string Dec2Frac(double dbl)
{
char neg = ' ';
double dblDecimal = dbl;
if (dblDecimal < 0)
{
dblDecimal = Math.Abs(dblDecimal);
neg = '-';
}
var whole = (int) Math.Truncate(dblDecimal);
if (whole == dbl) {
return String.Format("{0} because supplied value is not a fraction", dbl); //return no if it's not a decimal
}
string decpart = dblDecimal.ToString(CultureInfo.InvariantCulture).Replace(Math.Truncate(dblDecimal) + ".", "");
double rN = Convert.ToDouble(decpart);
double rD = Math.Pow(10, decpart.Length);
string rd = Recur(decpart);
int rel = Convert.ToInt32(rd);
if (rel != 0)
{
rN = rel;
rD = (int) Math.Pow(10, rd.Length) - 1;
}
//just a few prime factors for testing purposes
var primes = new[] {47, 43, 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2};
foreach (int i in primes) reduceNo(i, ref rD, ref rN);
rN = rN + (whole*rD);
return string.Format("{0}{1}/{2}", neg, rN, rD);
}
首先,我们注意十进制数是否为负数,然后获取十进制数的整数部分。然后,我们通过适当地将数字除以 10 的幂来简化小数部分。
接下来,我们使用 recur
方法检查它是否是循环小数,如果存在循环小数,该方法将以 string
格式返回循环小数。这是因为小数部分可能以零开头,如 1 / 11 所示。这里
private static string Recur(string db)
{
if (db.Length < 13) return "0";
var sb = new StringBuilder();
for (int i = 0; i < 7; i++)
{
sb.Append(db[i]);
int dlength = (db.Length/sb.ToString().Length);
int occur = Occurence(sb.ToString(), db);
if (dlength == occur || dlength == occur - sb.ToString().Length)
{
return sb.ToString();
}
}
return "0";
}
关注点
在开发这个类时,我发现 C# String
类不够强大(目前不再成立,因为 LINQ 提供了许多用于任何类型操作的方法)。例如,我找不到一种方法来查找 string
或字符在 string
中出现的次数,并且不得不自己开发一个,如 occurrence
方法。
private static int Occurence(string s, string check)
{
int i = 0;
int d = s.Length;
string ds = check;
for (int n = (ds.Length/d); n > 0; n--)
{
int si = ds.IndexOf(s, StringComparison.Ordinal);
if (si != -1)
{
i++;
ds = ds.Remove(si, d);
}
}
return i;
}
值得注意的是,此方法不会做你可能认为它所做的事情。此方法检查某个数字在另一个数字中出现的次数,以确定某个数字是否为循环小数。
历史
这是编写一个简单的分数转换类的第一次成功尝试,于 2011 年 3 月 6 日编写和发布。如果我以后对其进行改进,代码将会更新。