分数化小数

Fractions to Decimals

Write a program that will accept a fraction of the form N/D, 
where N is the numerator and D is the denominator and print the decimal representation.
If the decimal representation has a repeating sequence of digits, indicate the sequence by enclosing it in brackets.
For example, 1/3 = .33333333...is denoted as 0.(3), and 41/333 = 0.123123123...is denoted as 0.(123).
Use xxx.0 to denote an integer. 
Typical conversions are:

1/3     =  0.(3) 
22/5    =  4.4 
1/7     =  0.(142857) 
2/2     =  1.0 
3/8     =  0.375 
45/56   =  0.803(571428)

PROGRAM NAME: fracdec 
INPUT FORMAT 
A single line with two space separated integers, N and D, 1 <= N,D <= 100000. 
45 56

OUTPUT FORMAT 
The decimal expansion, as detailed above.
If the expansion exceeds 76 characters in length, print it on multiple lines with 76 characters per line.  
0.803(571428)

Description
写一个程序,输入一个形如N/D的分数(N是分子,D是分母),输出它的小数形式。如果小数有循环节的话,把循环节放在一对圆括号中。
例如, 1/3 = .33333333 写成0.(3) 41/333 = 0.123123123... 写成0.(123) 
用xxx.0 成表示整数典型的转化例子: 
 1/3 = 0.(3)
 22/5 = 4.4 
 1/7 = 0.(142857)
 2/2 = 1.0 
 3/8 = 0.375
 45/56 = 0.803(571428)
Input
单独的一行包括被空格分开的 N和D, 1 <= N,D <= 100000。
Output
小数的表示方法上面说的很明白了,如果输出的长度超过76个字符,每行输出76个。
Sample Input
45 56
Sample Output
0.803(571428)

程序中用到的库函数
(1)sprintf函数
功能:把格式化的数据写入某个字符串缓冲区。
头文件:在C中 <stdio.h>    或    在C++中 <cstdio>
原型:int sprintf( char *buffer, const char *format, [ argument] … );
参数列表
buffer:char型指针,指向将要写入的字符串的缓冲区。
format:格式化字符串。
[argument]...:可选参数,可以是任何类型的数据。
返回值:字符串长度(strlen)

strcat 只能连接字符串(一段以‘’结尾的字符数组或叫做字符缓冲,null-terminated-string),
但有时我们有两段字符缓冲区,他们并不是以’’结尾。比如许多从第三方库函数中返回的字符数组,
从硬件或者网络传输中读进来的字符流,它们未必每一段字符序列后面都有个相应的’’来结尾。
如果直接连接,不管是sprintf 还是strcat 肯定会导致非法内存操作,而strncat 也至少要求第一个参数是个null-terminated-string,那该怎么办呢?
我们自然会想起前面介绍打印整数和浮点数时可以指定宽度,字符串也一样的。比如:
char a1[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
char a2[] = {'H', 'I', 'J', 'K', 'L', 'M', 'N'};
如果:
sprintf(s, "%s%s", a1, a2); //Don't do that!
十有八九要出问题了。是否可以改成:
sprintf(s, "%7s%7s", a1, a2);
也没好到哪儿去,正确的应该是:
sprintf(s, "%.7s%.7s", a1, a2);//产生:"ABCDEFGHIJKLMN"
这可以类比打印浮点数的”%m.nf”,在”%m.ns”中,
m 表示占用宽度(字符串长度不足时补空格,超出了则按照实际宽度打印),n 才表示从相应的字符串中最多取用的字符数。
通常在打印字符串时m 没什么大用,还是点号后面的n 用的多。自然,也可以前后都只取部分字符:
sprintf(s, "%.6s%.5s", a1, a2);//产生:"ABCDEFHIJKL"

在许多时候,我们或许还希望这些格式控制符中用以指定长度信息的数字是动态的,而不是静态指定的,
因为许多时候,程序要到运行时才会清楚到底需要取字符数组中的几个字符,这种动态的宽度/精度设置功能在sprintf 的实现中也被考虑到了,
sprintf 采用”*”来占用一个本来需要一个指定宽度或精度的常数数字的位置,
同样,而实际的宽度或精度就可以和其它被打印的变量一样被提供出来,于是,上面的例子可以变成:
sprintf(s, "%.*s%.*s", 7, a1, 7, a2);
或者:
sprintf(s, "%.*s%.*s", sizeof(a1), a1, sizeof(a2), a2);


(2)memset
功能:将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值, 块的大小由第三个参数指定。
       这个函数通常为新申请的内存做初始化工作, 其返回值为指向S的指针。
头文件:在C中 <string.h>    在C++中 <cstring>
原型: void *memset(void *s, char ch, size_t n);
函数解释:将s中前n个字节 (typedef unsigned int size_t)用 ch 替换并返回 s 。
memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法。

方法(1)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define MAX 100010

int rm[MAX];    //定义整型数组,来记录对应与下标相等的余数是否出现(-1表示未出现,其它表示已出现过)
char buf[MAX];  //定义字符数组
char dev[MAX];  //定义字符数组,存小数部分
int counter;    //初始化为0

int main(void)
{
    int m, n; //m表示分子   n表示分母
    int i;
    
    cin>>m;  //输入分子
    cin>>n;  //输入分母
    while(m>=0&&n>0)
    {
        cout<<m<<"/"<<n<<"=";
        sprintf(buf, "%d.", m/n); //将m/n的整数部分叫上小数点“.”放到buf字符数组中
        
        memset(rm, -1, sizeof(rm)); //将rm的全部数组元素都设置成-1
        
        m = m % n;  //m等于m除n的余数
        
        dev[0] = '0'; //dev中的第一个元素为'0'
        
        for(i = 0; ; i++){
            if(m == 0) //如余数一开始为0,表示能够整除, 则直接输出整数部分加".0";如果计算过程中余数为0则表示没有循环节,直接输出buf中内容加dev中内容
            {
                sprintf(buf + strlen(buf), "%s", dev);  //将dev中的内容加入到先前buf的尾部
                break; //跳出循环
            }
            
            if(rm[m] != -1)  //rm[m]不等于-1时,即余数m已经出现过一次了
            {
                sprintf(buf + strlen(buf), "%.*s(%s)", rm[m], dev, dev + rm[m]); //将dev中的rm[m]个元素连接dev+rm[m]串内容加入到先前buf的尾部
                break; //跳出循环
            }
            
            rm[m] = i; 
            m *= 10; //每次余数乘10为下一次除法做准备
            dev[counter++] = m / n + '0';  //对应整除部分并转换为对应的字符
            m = m % n;  //m存余数
            
        }
        
        
        for(i = 0; i<strlen(buf); i+=76) //buf中的每76个字符作为一行输出
        {
            printf("%.76s
", buf + i);
        }    
        
         memset(rm, -1, sizeof(rm)); //将rm的全部数组元素都设置成-1
         memset(buf, '', sizeof(buf)); //将buf的全部数组元素都设置成空字符
         memset(dev, '', sizeof(dev)); //将rm的全部数组元素都设置成空字符
         counter=0; //重新赋值为0
         cin>>m>>n;  //重新输入分子和分母
    }
    
    return 0;
}

方法(2)

#include<stdio.h>
#include<math.h>
#include<string.h>
int flag[100002],ans[100010];
int main()
{
    int i,n,d,num,len;
    memset(flag,0,sizeof(flag));
    scanf("%d%d",&n,&d);
    num=1;
    printf("%d.",n/d);
    if(n>d)len=int(log(n/d)/log(10)+2.00001);
    else len=2;
    n=n%d;
    while(!flag[n]&&n)
    {
        flag[n]=num;
        n=n*10;
        ans[num++]=n/d;
        n=n%d;
    }
    if(num==1)printf("0");
    for(i=1;i<num;i++)
    {
        if(flag[n]==i)
        {
            len++;
            printf("(");
        }
       
        printf("%d",ans[i]);
        len++;
    }
    if(n)printf(")");
    printf("
");
    return 0;
}

补充有关分数化小数中循环节的相关知识【转】

1、循环节的位数一定小于除数,最多也要比除数小1。

       这一点很容易理解。因为余数总是比除数小,如果除数是ɑ,余数只能是1,2,3,…,ɑ-1,所以最多在除了ɑ-1位以后,继续除下去,余数肯定会重复出现,这样就形成了循环。

       2、循环节的位数如果是偶数,把循环节分成位数相等的两段,两段对应数字的和一定是9(不能证明)。

       比如,循环节“142857”可以分成“142”和“857”两段,1+8=9,4+5=9,2+7=9。循环节“90”可以分成“9”和“0”两段,9+0=9。循环节“0434782608695652173913” 可以分成“04347826086”和“95652173913”两段,0+9=9,4+5=9,3+6=9,4+5=9,7+2=9,8+1=9,2+7=9,6+3=9,0+9=9,8+1=9,6+3=9。

       3、一个最简真分数q/p,如果分母p能够整除一连串最少n个9,用所得的商乘分子q,得数(应该有n位,不足n位时在前面补0)就是这个最简真分数化成纯循环小数时的循环节。

证明:假设有一个纯循环小数x=0.ɑ1ɑ2ɑ3ɑ1ɑ2ɑ3…。由此可以推出1000x=ɑ1ɑ2ɑ31ɑ2ɑ3ɑ1ɑ2ɑ3…,1000x-x=ɑ1ɑ2ɑ3999x=ɑ1ɑ2ɑ3x=ɑ1ɑ2ɑ3/999。说明这个纯循环小数可以化成分数,分子就是它的循环节,分母是与循环节位数相同的一连串3个9。把这个分数化成最简分数,ɑ1ɑ2ɑ3/999=q/p,于是ɑ1ɑ2ɑ3999q/p,ɑ1ɑ2ɑ3在形式上是整数,而q不能被p整除,所以999就一定能被p整除,因此ɑ1ɑ2ɑ3999÷q。并且因为ɑ1ɑ2ɑ33位,得数也应该有3位,不足3位时就要在前面补0。这样就找到了确定循环节的方法。

原文地址:https://www.cnblogs.com/beautiful-code/p/5239421.html