题目不难,高精度除法+放缩法
大致题意为:给定一个8进制数的小数(包括0和1),要将其转化为10进制小数。要将十进制小数的小数点后保留8进制小数小数点后位数的3倍即可。
题意很明确。分析如下:
8进制小数转化为10进制小数,具体规则很简单。例如现在有0.123(8),要转化为10进制,则为1*8^-1 + 2*8^-2 + 3*8^-3。可以依据这个作为题目的突破点。
按照上面的转化规则,可以将8进制小数转化为两个十进制数相除。例如上面可以转化为(1*8^2+2*8^1+3)/8^3。问题就转化为高精度整数除法运算了。同时由于要求保留小数点后规定数字,所以这里必须实现将分子放大,保证满足商可以有题目要求的精度位数,最后将商缩小,取满足题目的精度位数即可。
另外就是注意0和1要特别处理,其实也很简单,同时分子分母要使用__int64类型,数据量比较大,详见注释。
下面是代码:168K+0MS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Max 1010
char Input[Max/10];
int result[Max]; //保存商结果
int a[Max];//除数
int b[Max];//被除数
int alen,blen; //长度
int num,len; //num为放大位数,len为输入的浮点数位数(包括小数点)
__int64 son,parent;
__int64 Power(int x){ //计算8^x
__int64 temp=1;
for(int i=0;i<x;i++)
temp*=8;
return temp;
}
int Substract(int *p,int *q,int lenp,int lenq){ //高精度减法,-1表示不够减,0表示相等,>0表示正常减法后被减数长度
if(lenp<lenq) //若前者长度较小
return -1;
else if(lenp==lenq){ //长度相同时,继续比较大小
for(int i=lenp-1;i>=0;i--){
if(p[i]<q[i]) return -1; //若相同位置前者比较小,则说明不够减
else if(p[i]>q[i]) break;
}
} // 相等或大于同一处理
for(int i=0;i<lenp;i++){ //高精度减法核心代码
p[i]-=q[i];
if(p[i]<0){ //借位
p[i]+=10;
p[i+1]-=1;
}
}
int i;
for(i=lenp-1;i>=0;i--) // 清0
if(p[i]>0)
break;
if(i>=0) // 若不相等则返回结果长度
return i+1;
else //相等返回0
return 0;
}
void cal(){ // 高精度除法运算
memset(a,0,sizeof(a)); // 除数清0
memset(b,0,sizeof(b)); //被除数清0
memset(result,0,sizeof(result)); // 结果清0
int i,j;
for(i=0;i<num;i++) // 放大被除数,是商满足题目精度,低位置0
b[i]=0;
while(son>0){ // 处理高位
b[i++]=son%10;
son/=10;
}
blen=i; // 长度
i=0;
while(parent>0){ // 赋值除数
a[i++]=parent%10;
parent/=10;
}
alen=i;
int ntime=blen-alen; //位数差
for(i=blen-1;i>=ntime;i--) // 右移除数,低位补零
a[i]=a[i-ntime];
while(i>=0){
a[i]=0;
i--;
}
alen=blen; //使长度相同
for(i=0;i<=ntime;i++){ //高精度除法核心代码
int ntemp;
while((ntemp=Substract(b,a+i,blen,alen-i))>=0){ // 若够减除数*10^(ntime-i)
blen=ntemp; //减之后被除数的长度
result[ntime-i]++; //相应为增加
}
}
for(i=0;i<Max;i++) // 查找尾部0位置
if(result[i]>0)
break;
printf("%s [8] = ",Input);
printf("0.");
int Sum=0; //计数,小数点后保留位数
for(j=num-1;j>=i;j--){ //依次输出小数点后数字
printf("%d",result[j]);
Sum++;
if(Sum==(len-2)*3) //若已经到达最大精度,则直接退出
break;
}
printf(" [10]\n");
}
int main(){
while(scanf("%s",Input)!=EOF){
getchar();
len=strlen(Input); //求长度
int index=0,dnum=0; // 分别标记小数点后第一个不为0的数字下标, 计数小数点后位数(包括尾部0)
bool trag=true; // 标记
son=0; //分子初始化为0
for(int i=len-1;i>=0;i--){ //试探小数点字符串
if(Input[i]=='.') //若为小数点,直接退出
break;
if(trag && Input[i]!='0'){ //若为小数点后第一个不为0的数字
trag=false; // 置标记
parent=Power(i-1); // 赋值分母值
son+=(Input[i]-'0'); // 累加分子值
index=i; //标记该下标
}
else if(!trag && Input[i]!='0' && Input[i]!='.') //处理小数点后第一个不是0的数字出现以后,且不是小数点
son+=((Input[i]-'0')*Power(index-i)); //累加分子
dnum++; //累计小数点后数字个数
}
if(son==0){ //若为1或0特殊情况,特殊处理
printf("%s [8] = %s",Input,Input);
for(int i=0;i<2*dnum;i++)
printf("0");
printf(" [10]\n");
}
else{ //否则一般处理
num=(len-2)*5; // 设置分子扩大位数*10^num
cal(); //计算十进制小数
}
}
return 0;
}