題目不難,高精度除法+放縮法
大致題意爲:給定一個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;
}