一個對於小數四捨五入的簡單算法
聲明:對於解題,應該會有更爲簡便的算法,通過測試點即可,本算法可以提供一種參考,是一個通用的關於帶有小數的四捨五入算法,本人學生黨,手寫不易,不喜勿噴,謝謝,也希望有各界大神批評賜教
-
有很多算法新手可能會遇到一種輸出的限制,不僅要保留規定的小數位,而且要求要四捨五入進位,如下某OJ的題目,要求是輸出結果保留兩位小數,小數點後第三位四捨五入到小數點後的第二位,但小數點後總共輸出6位(即最後需要輸出4個0)
-
分析1:很多人會首先想到printf的%.mf(對於本題保留兩位
printf("%.2f",var);
)的辦法,這樣的辦法本身帶有取捨方法,但這種取捨方法並不能完美的迎合要求,畢竟題目的要求是四捨五入,而此種方法的原則是“四捨六入五成雙”,除非可以保證判斷進位的位是5的情況下後面的位非0或者5後無有效數字前一位爲奇數(以上數位均指十進制數位)詳見百度百科,說起來比較抽象,請看下面的例子(四捨六入就不舉例子了):
double test = 1.2351;
printf("%.2f\n",test);
//這種情況屬於進位的情況,在5後面有非零數值,進位
test = 1.235;
printf("%.2f\n",test);
//這種情況屬於進位的情況,在5後面是0,無有效數字,但前面爲3奇數,進位
test = 1.245;
printf("%.2f\n",test);
//這種情況屬於舍位的情況,在5後面爲0,無有效數字,但前面是4,爲偶數,舍位
- 輸出結果:
1.24
1.24
1.25
- 分析2: 有人想到了C++中的保留小數的方法,
cout<<setiosflags(ios::fixed)<<setprecision(2);//需要頭文件#include <iomanip>
這種方法非常有效,既能保留指定位數,又能進行四捨五入,但是,一來某些場合並不能使用C++的環境硬性要求使用C,那麼這種輸出方法無法使用;二來需要單獨記憶這個格式,規律性不強,難以記牢,易忘;再者,在負數的情況下,無論保留位後是什麼,均直接丟棄。例如下列情況:
cout<<setiosflags(ios::fixed)<<setprecision(2)<<-1.115<<endl;
cout<<setiosflags(ios::fixed)<<setprecision(2)<<-1.114<<endl;
cout<<setiosflags(ios::fixed)<<setprecision(2)<<-1.110<<endl;
- 輸出結果:
-1.11
-1.11
-1.11
在描述算法之前,有必要先對sprintf與sscanf進行介紹。因爲筆者在算法中用到他們,模擬了肉眼觀察的過程,沒有涉及複雜運算。
API | 參數 | 功能 |
---|---|---|
sprintf | char*,const char*,… | 將第三個參數的內容按第二個參數的格式傳輸到第一個參數的字符串(數組)中 |
sscanf | const char*,const char*,… | 將第一個參數中的字符串(數組)以第二個參數的格式傳輸到第三個參數的變量中 |
上面以筆者的理解通俗的描述了一遍,很多的情況下應用於將字符串與數值變量之間的轉換,如果理解起來有困難,就是筆者的描述問題,詳見此處博客總結或自行百度。
- 下面是筆者的算法描述(未優化但易於理解):
double decimalFormat5Up(double srcNumber , int decimalBitNumber,int integerBitNumber){
/*
Description: To complete the work rounding decimal number.The rounding execute
rule is when the compare bit over five to carry bit, or drop
the follow bits.
Parameters : srcNumber: the number what you wanna to deal with
decimalBitNumber: the bits count what you wanna to reserve
integerBitNumber: the bits count your number's integer part
Date : 2020-1-7
Author : Moresweet
CSDN ID :qq_38853759
*/
char temp[100]; //the char array transfer the middle result
int flag = 0;
if(srcNumber < 0){ //deal with negative number
srcNumber = -srcNumber;
flag = 1;
}
sprintf(temp,"%f",srcNumber);
int length = strlen(temp),i;
if(decimalBitNumber > length || decimalBitNumber < 0){ //the error situation to return origin number
return srcNumber;
}
if(temp[decimalBitNumber+integerBitNumber+1]>= ('0'+5) ){
//carry bit when the current bit value is over five
srcNumber += (double)1/pow(10,decimalBitNumber);
sprintf(temp,"%f",srcNumber);
} //array index add two to adjust to avoid the error bought by the integer and decimal point
for(i=decimalBitNumber+integerBitNumber+1;i<length;i++){ //these follow bits is set zero
temp[i] = '0';
}
if(flag){
for(i=length - 1;i>=0;i--){
temp[i+1] = temp[i];
}
temp[0] = '-';
}
sscanf(temp,"%lf",&srcNumber);
return srcNumber;
}
- 可能註釋看起來比較噁心,讀者可以自行將註釋去掉,下面進行簡要的解釋,首先是重要的三個參數,第一個參數srcNumber是你要處理的數據,原數據,第二個參數decimalBitNumber是你的數據要保留的小數位數,第三個參數integerBitNumber是你的數據的整數位數,如28.33,整數位爲2位,接下來temp數組中可用於存儲中間結果,由於負數的四捨五入在不考慮符號位的情況下,舍入規則與正數是一樣的,故先對負數進行取反處理,並對flag標誌位進行置1操作,最後進行處理,程序先將數據轉換成字符串,轉儲入temp字符數組,然後對temp字符數組進行操作,根據整數位數+保留小數位數+1+1-1(+1是跳過小數點,再+1是爲了找到需要判斷的那一位,也即保留位的最後一位的後一個,再-1是爲了將邏輯序號與物理序號對其,衆所周知數組的標號是從0開始)找到需要判斷是否進位的數位,例如保留兩位小數,那麼對於1.546來說,需要判斷的是6,根據這個數是否大於等於5進行判斷是否進行,顯然6大於5,應進位爲1.55;那麼程序根據這個邏輯,取出判斷位的字符與字符‘5’的ASCII碼進行比較就能判斷出此位是否大於5,若此位大於等於5,滿足進位條件,將原數加上10的-n次冪(n爲保留小數的位數),完成進位,舉個例子,若保留兩位小數,1.765滿足進位條件,+0.01修正爲1.775,重新轉儲到temp字符數組,再進行後續操作;若此位小於5不做處理,最後將保留之後的所有數位置爲字符0,即丟棄後續位,然後使用sscanf將temp字符數組中的最終處理結果作爲double數值存入原數變量中,返回double型的最終結果。
- 算法的使用
- 示例代碼(主函數及函數聲明)
#include <stdio.h>
#include <string.h>
#include <math.h>
double decimalFormat5Up(double , int ,int );
int main()
{
double testDb;
scanf("%lf",&testDb);
//testDb = testDb*9/5+32; //此處爲東華大學OJ的一道溫度轉換題,消除註釋即可AC
int n = 1,count = 0;
while((int)testDb / n){ //判斷整數位
n=n*10;
count++;
}
testDb = decimalFormat5Up(testDb,count,2);
printf("%.6f\n",testDb);
}
- 輸出
第一組數據:
29.9999
30.000000
第二組數據:
80.006
80.010000
希望有興趣的讀者認真閱讀思路和代碼並不難,下面是優化後的雙參數算法,思路與上面相同,有隻想使用功能的讀者可直接複製套用。
double decimalFormat5Up1(double srcNumber , int decimalBitNumber){
/*
Description: To complete the work rounding decimal number.The rounding execute
rule is when the compare bit over five to carry bit, or drop
the follow bits.
Parameters : srcNumber: the number what you wanna to deal with
decimalBitNumber: the bits count what you wanna to reserve
Date : 2020-1-7
Author : Moresweet
CSDN ID :qq_38853759
*/
char temp[100]; //the char array transfer the middle result
int n = 1,integerBitNumber = 0,flag = 0;//integerBitNumber: the bits count your number's integer part
while((int)srcNumber / n){
n=n*10;
integerBitNumber++;
}
if(srcNumber < 0){ //deal with negative number
srcNumber = -srcNumber;
flag = 1;
}
sprintf(temp,"%f",srcNumber);
int length = strlen(temp),i;
if(decimalBitNumber > length || decimalBitNumber < 0){ //the error situation to return origin number
return srcNumber;
}
if(temp[decimalBitNumber+integerBitNumber+1]>= ('0'+5) ){
//carry bit when the current bit value is over five
srcNumber += (double)1/pow(10,decimalBitNumber);
sprintf(temp,"%f",srcNumber);
} //array index add two to adjust to avoid the error bought by the integer and decimal point
for(i=decimalBitNumber+integerBitNumber+1;i<length;i++){ //these follow bits is set zero
temp[i] = '0';
}
if(flag){
for(i=length - 1;i>=0;i--){
temp[i+1] = temp[i];
}
temp[0] = '-';
}
sscanf(temp,"%lf",&srcNumber);
return srcNumber;
}
- 示例主程序
int main()
{
double testDb;
scanf("%lf",&testDb);
testDb = decimalFormat5Up1(testDb,2);
printf("%.6f\n",testDb);
}
筆者水平有限,若有bug盡請大神批評指正,謝謝!