簡體   English   中英

來自C程序的Bizzare行為:: Kernighan和Ritchie練習2-3

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM