2015-06-27 54 views
1

我正在寫一個函數,它假設已將float「length」傳遞給它,然後顯示一個類似於輸出的計時器。浮點「長度」意味着在幾分鐘內。浮點值未被正確存儲

當我運行它時,輸出應該是:02:03:27:00。相反,它顯示02:03:26:100,雖然技術上正確,但它是A)不是它應該如何顯示和B)顯示在某個地方有一個錯誤,可能會在未來導致不良結果。

用計算器手動檢查後發現所有的數學都是合理的。然後,我評論了格式爲零的部分,以查看是否導致錯誤並且問題仍然存在。然後,我在每次計算後都會放入printf,發現「length」設置爲123.45時,它被存儲爲123.449997?

我不知道它是這樣做的。而且因爲我不知道這種情況或發生的方式,所以我不能爲它寫一個可靠的解決方案。

int main() 
{ 

float length; 
float working; 
int hour; 
int min; 
int sec; 
int centi_sec; 
char hzero[2]; 
char mzero[2]; 
char szero[2]; 
char czero[2]; 

    length=123.45; 
    working=floor(length); 
    working=(length-working)*60; 
    sec=floor(working); 
    working-=floor(working); 
    centi_sec=(working*100)+.5; 
    working=floor(length); 
    hour=floor((working/60)); 
    min=working-(hour*60); 
    if(hour<10){ 
     hzero[0]='0'; 
     hzero[1]=""; 
    } 
    else if(hour==0){ 
     hzero[0]='0'; 
     hzero[1]='0'; 
    } 
    else{ 
     hzero[0]=""; 
     hzero[1]=""; 
    } 
    if(min<10){ 
     mzero[0]='0'; 
     mzero[1]=""; 
    } 
    else if(min==0){ 
     mzero[0]='0'; 
     mzero[1]='0'; 
    } 
    else{ 
     mzero[0]=""; 
     mzero[1]=""; 
    } 
    if(sec<10){ 
     szero[0]='0'; 
     szero[1]=""; 
    } 
    else if(sec==0){ 
     szero[0]='0'; 
     szero[1]='0'; 
    } 
    else{ 
     szero[0]=""; 
     szero[1]=""; 
    } 
    if(centi_sec<10){ 
     czero[0]='0'; 
     czero[1]=""; 
    } 
    else if(centi_sec==0){ 
     czero[0]='0'; 
     czero[1]='0'; 
    } 
    else{ 
     czero[0]=""; 
     czero[1]=""; 
    } 
    printf("%s%d:%s%d:%s%d:%s%d\n", hzero, hour, mzero, min, szero, sec, czero, centi_sec); 
    system("pause"); 

} 

我也寫一個簡短的程序只是這是不是問題,因爲我曾錯過了完整的程序的東西的影響,它有同樣的問題臉頰。

int main() 
{ 

float length=123.45; 

    printf("%f\n", length); 
    system("pause"); 

} 

P.S.當我使用printf來解決問題時,我發現printf正在搞亂零格式。沒有太大的問題,因爲當我刪除它們時,格式化回到它應該如何。儘管如此,printf的格式化並沒有任何意義。如果任何人也可以提供這個答案,這將不勝感激。

在此先感謝。

+0

你有一個潛在的問題,因爲'floor()'不返回'int'它返回'double'。也不是這個'hzero [0] =「」;'生成編譯器警告? –

+0

你有一個很大的非科學數據類型可以使用 – Drew

回答

1

您已分配一個二進制浮點數值爲十進制實數值。 二進制浮點數不能完全表示所有實數十進制值。

單精度二進制浮點有利於精確表示大約6位十進制有效數字,123.449997是9位數;所以你已經超出了承諾的精度。默認情況下,%f格式說明符顯示6個小數位,但在這種情況下超出了可用精度。

要麼使用顯示合理精度的格式說明:

printf("%.3f\n", length) ; 

,或者使用double這是很好的15個小數顯著數字。

對於高內存帶寬和硬件浮點單元(即所有現代臺式計算機)的目標,不使用雙精度的原因很少。如果您正在處理真正的海量數據,並且需要減少處理時間並且不需要精度,則單精度非常有用。

+0

謝謝,它現在應該如何工作。 – Ulrick

0

代碼中的問題與浮點精度無關,精度有限制,並且由於舍入操作引入了錯誤,但小數點後兩位應該沒有問題,也不會出現問題不管內部值打印爲123.449997,如果你做

printf("%.2f\n", 123.449997); 

123.45將被打印,並且也涉及價值的任何算術運算,其結果將是精度,2足夠的小數位正確。

你最重要的問題是您的字符串不能是這樣的字符串,因爲沒有空間終止'\0'

而且數學是錯誤太多,因爲如果centi_sec大於或等於則應該加100一秒,100應從centi_sec中減去,這同樣適用於secmin,等等。

這些

char hzero[2]; 
char mzero[2]; 
char szero[2]; 
char czero[2]; 

應該讀

char hzero[3]; 
char mzero[3]; 
char szero[3]; 
char czero[3]; 

你也應該不會重複自己,用一個函數

#include <math.h> 
#include <stdio.h> 

void zeropad(int value, char str[3]) 
{ 
    if (value < 10) 
    { 
     str[0] = '0'; 
     str[1] = value + '0'; 
    } 
    else 
    { 
     str[0] = (value - value % 10)/10 + '0'; 
     str[1] = value % 10 + '0'; 
    } 
    str[2] = '\0'; 
} 

int main() 
{ 
    float length; 
    float working; 
    int hour; 
    int min; 
    int sec; 
    int centi_sec; 
    char hzero[3]; 
    char mzero[3]; 
    char szero[3]; 
    char czero[3]; 

    length = 123.45; 
    working = floor(length); 
    working = (length - working) * 60; 
    sec  = floor(working); 
    working -= floor(working); 
    centi_sec = (working * 100) + .5; 
    working = floor(length); 
    hour  = floor(working/60); 
    min  = working - (hour * 60); 

    if (centi_sec >= 100) 
    { 
     sec  += 1; 
     centi_sec -= 100; 
    } 

    if (sec >= 60) 
    { 
     min += 1; 
     sec -= 60; 
    } 

    if (min >= 60) 
    { 
     hour += 1; 
     min -= 60; 
    } 

    zeropad(hour, hzero); 
    zeropad(min, mzero); 
    zeropad(sec, szero); 
    zeropad(centi_sec, czero); 

    printf("%s:%s:%s:%s\n", hzero, mzero, szero, czero); 
} 
+1

這可能是發佈的代碼有問題,但它與提出的問題無關,應該作爲評論發佈(如果有的話)。 – Clifford

+0

您的示例中也存在長度問題,但void zeropad確實解決了printf混淆格式的問題。我仍然對c知道,並且我不太瞭解zeropad中正在發生的一切。我得到了一些,但不是全部。另外,我仍然不明白爲什麼printf首先會搞亂格式化? – Ulrick

0

浮點數只有這麼多的準確性,而不是所有的數字可以在浮點變量中精確表示。

我的第一個建議是傳遞一個包含4個整數領域

int hours 
int minutes 
int seconds 
int fractionSecondX100 

從而避免整個問題浮筒設置較小的結構。但是,如果您必須使用浮點值,那麼google:「如何處理C中的浮點值」,這會在google之後返回很多'點擊數',請閱讀幾個引用的網頁,以便您瞭解處理花車並且知道會發生什麼。

1

由於數學原因,小數部分的二進制表示在所有情況下都不能精確。你可以閱讀更多關於這個here

爲了避免這個問題,你需要添加一半的最小單位的輸入。 在這種情況下,這將是1.0/60/100/2

length = 123.45; 
const float epsilon = 1.0/60/100/2; 
length += epsilon; 

你試圖做同樣的事情與

centi_sec=(working*100)+.5; 

但這隻有一個沒有其他數字上centi_sec效果,。將其更改回

centi_sec=(working*100); 

也請按照@iharob的建議更改陣列大小。

編輯:您可以完全避免陣列:

#include <math.h> 
#include <stdio.h> 

int main() 
{ 

const float epsilon = 1.0/60/100/2; 
float length; 
float working; 
int hour; 
int min; 
int sec; 
int centi_sec; 

    length=123.45; 
    length += epsilon; 
    working=floor(length); 
    working=(length-working)*60; 
    sec=floor(working); 
    working-=floor(working); 
    centi_sec=(working*100); 
    working=floor(length); 
    hour=floor((working/60)); 
    min=working-(hour*60); 
    printf("%02d:%02d:%02d:%02d\n", hour, min, sec, centi_sec); 
// system("pause"); 

} 

這工作。

+0

這實際上使之不那麼準確。 – Ulrick

+0

我做了一個更簡單的程序版本,它適用於我。 – alain

+0

問題是float被存儲爲123.449997,在你的代碼中它被存儲爲124.450081,這是我的不太準確。但它確實完全消除了對零陣列的需求。所以這是一個很大的改進。 – Ulrick