[英]Bizzare behavior from C program:: Kernighan & Ritchie exercise 2-3
所有。
我已經編寫了一個程序來解決Kernighan&Ritchie的練習2-3,其在測試期間的行為(IMHO)非常不直觀。
問題規范要求編寫一個將十六進制值轉換為十進制等效值的程序。 我編寫的代碼對於較小的十六進制值可以很好地工作,但是對於較大的十六進制值,事情會變得有些奇怪。 例如,如果我輸入0x1234 ,則在另一端彈出十進制值4660 ,這恰好是正確的輸出(該代碼也適用於字母,即0x1FC- > 508 )。 另一方面,如果我要輸入一個較大的十六進制值,例如0x123456789ABCDEF ,則應該 輸入81985529216486895 ,盡管我輸入的是81985529216486896 ( 相差一個數字!)。
轉換錯誤不一致,有時十進制值過高而其他時候過低。 通常,較大的十六進制值會導致十進制輸出中的位數不正確。
這是我的完整程序:
/*Kernighan & Ritchie's Exercise 2-3
Write a function 'htoi' which converts a string of hexadecimal digits (including an
optional 0x or 0X) into its equivalent integer value.
*/
#include <stdio.h>
#define MAXLINE 1000 //defines maximum size of a hex input
//FUNCTION DEFINITIONS
signed int htoi(char c); //converts a single hex digit to its decimal value
//BEGIN PROGRAM////////////////////////////////////////////////////////////
main()
{
int i = 0; //counts the length of 'hex' at input
char c; //character buffer
char hex[MAXLINE]; //string from input
int len = 0; //the final value of 'i'
signed int val; //the decimal value of a character stored in 'hex'
double n = 0; //the decimal value of 'hex'
while((c = getchar()) != '\n') //store a string of characters in 'hex'
{
hex[i] = c;
++i;
}
len = i;
hex[i] = '\0'; //turn 'hex' into a string
if((hex[0] == '0') && ((hex[1] == 'x') || (hex[1] == 'X'))) //ignore leading '0x'
{
for(i = 2; i < len; ++i)
{
val = htoi(hex[i]); //call 'htoi'
if(val == -1 ) //test for a non-hex character
{
break;
}
n = 16.0 * n + (double)val; //calculate decimal value of hex from hex[0]->hex[i]
}
}
else
{
for(i = 0; i < len; ++i)
{
val = htoi(hex[i]); //call 'htoi'
if(val == -1) //test for non-hex character
{
break;
}
n = 16.0 * n + (double)val; //calc decimal value of hex for hex[0]->hex[i]
}
}
if(val == -1)
{
printf("\n!!THE STRING FROM INPUT WAS NOT A HEX VALUE!!\n");
}
else
{
printf("\n%s converts to %.0f\n", hex, n);
}
return 0;
}
//FUNCTION DEFINITIONS OUTSIDE OF MAIN()///////////////////////////////////
signed int htoi(char c)
{
signed int val = -1;
if(c >= '0' && c <= '9')
val = c - '0';
else if(c == 'a' || c == 'A')
val = 10;
else if(c == 'b' || c == 'B')
val = 11;
else if(c == 'c' || c == 'C')
val = 12;
else if(c == 'd' || c == 'D')
val = 13;
else if(c == 'e' || c == 'E')
val = 14;
else if(c == 'f' || c == 'F')
val = 15;
else
{
;//'c' was a non-hex character, do nothing and return -1
}
return val;
}
pastebin: http : //pastebin.com/LJFfwSN5
對這里發生的事情有什么想法嗎?
您可能超出了double
可以存儲整數的精度。
我的建議是將代碼更改為對結果使用unsigned long long
; 並在此處添加溢出檢查,例如:
unsigned long long n = 0;
// ...
if ( n * 16 + val < n )
{
fprintf(stderr, "Number too big.\n");
exit(EXIT_FAILURE);
}
n = n * 16 + val;
我的小於檢查之所以有效,是因為當無符號整數類型溢出時,它們會歸零。
如果您想unsigned long long
增加比unsigned long long
更多的精度,那么您將不得不采用更高級的技術(可能超出了K&R第2章的范圍,但是一旦完成本書,您就可以重新訪問)。
注意 您還需要#include <stdlib.h>
,如果你把我的建議exit
; 並且不要忘記在最終的printf
%llu
%.0f
更改為%llu
。 另外,獲取輸入(K&R涵蓋)的一種更安全的方法是:
int c;
while((c = getchar()) != '\n' && c != EOF)
第一次在ideone上運行代碼時,我遇到了段錯誤,因為我沒有在stdin
的末尾插入換行符,因此此循環一直將EOF推入hex
直到緩沖區溢出為止。
這是浮點誤差的經典示例。
與您將看到的大多數浮點錯誤示例不同,這顯然與非二進制分數或非常小的數字無關; 在這種情況下,浮點表示近似於很大的數字,精度越高,精度越高。 原理與寫“ 1.6e10”的意思相同,意思是“大約16000000000”(我想我在那里算過零!),而實際數字可能是16000000001。
實際上,比起使用相同大小的整數,精度實際上要早一點用完,因為浮點變量的寬度的一部分只能用於表示整數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.