模拟手算计算小数循环部分,长除法的使用例如3 / 7
3 / 7 = 0 … 3
30 / 7 = 4 … 2
20 / 7 = 2 … 4
40 / 7 = 5 … 5
50 / 7 = 7 … 1
10 / 7 = 1 … 3
30 / 7 = 4 … 2
可以看到,已经出现循环0.(42571)。
每一次的除法时都有被除数和余数,当除数重复出现时就表示出现循环节。
如果除数出现第二次,例如上例中30出现的时刻,就是第二次循环开始的位置。
所以需要记住每一次的商及除数的位置,除数不够除的时候,补零。
#include <iostream>
#include <vector>
#include <cstring>
#include <string>
#include <map>
#include <cstdio>
#include <algorithm>
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
using namespace std;
map<int, int> Pos;//用于计算除数的相应位置
/*
* 从除数和被除数的关系得到循环小数
* @param n 除数
* @param d 被除数
* @param ans 小数部分
* @param r 循环部分长度
*/
void solve(int n, const int d, string& ans, int& r)
{
Pos.clear();
ans = ".";//初始位置是1,ans.size() = 1
while (true)
{
n *= 10;
int p = Pos[n];
if (p == 0) Pos[n] = ans.size();//Pos[n], 表示除数是第几个的位置
else
{ //如果除数再次出现,Pos[n]此时大于0
r = ans.size() - p;//找到循环节
if (r > 50) {ans.erase(p+50); ans += "...";}//删除ans的50位置后字符
ans.insert(p, "(");
ans += ")";
break;
}
if (n < d) {ans += '0'; continue;}
int div = n / d, mod = n % d;
ans += (char)(div + '0');//添加手算小数
n = mod;
if (n == 0) {ans += "(0)"; r = 1;break;}
}
}
int main()
{
int a, b;
while (~scanf("%d%d", &a, &b))
{
string ans = ".(0)";
int r = 1;
if (a % b) solve(a % b, b, ans, r);
printf("%d/%d = %d%s\n", a, b, a/b, ans.c_str());
printf(" %d = number of digits in repeating cycle\n\n", r);
}
return 0;
}