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