问题
This is my second question for this code. this code comes from a modified script on page 66 of "Sams Teach Yourself C Programming" 7th edition. After copying the script out of the book, I modified it (just a little) to make it more interesting.
- I added some additional constants and variables and such to enable it to accept a larger input/output value.
- because the input/output is type "unsigned long int", which has a range of 0 - 2147483647, I set that to be the LIMIT.
- Then I added an "else" at the very end of the program to deal with all inputs above that value. I just wanted to do an experiment to see if it would register, values that exceeded the range.
- the first time I posted a question about a math error in the code that I had failed to find on my own. That was resolved.
Here are some sample outputs:
[bad_cat@KittyLitter LearningCode]$ gcc SamsC.04.seconds.c
[bad_cat@KittyLitter LearningCode]$ ./a.out
Enter the number of seconds ( > 0, < 2147483647 ):
2147483646
2147483646 seconds is equal to 68 y 35 d, 3 h, 14 m, and 6 s
[bad_cat@KittyLitter LearningCode]$ ./a.out
Enter the number of seconds ( > 0, < 2147483647 ):
2147483647
2147483647 seconds is equal to [bad_cat@KittyLitter LearningCode]$ ./a.out
Enter the number of seconds ( > 0, < 2147483647 ):
2147483650
-2147483646 seconds is equal to error: -2147483646 is an excessive amount of seconds.
range for seconds must be between 0 and 2147483647!
If number of seconds exceeds 2147483647, then it is beyond range for type 'int'.
EXITING seconds program.
[bad_cat@KittyLitter LearningCode]$ ./a.out
Enter the number of seconds ( > 0, < 2147483647 ):
9876543210
1286608618 seconds is equal to 40 y 291 d, 7 h, 16 m, and 58 s
[bad_cat@KittyLitter LearningCode]$
Here's the code:
/* Illustrates the modulus operator. */
/* inputs a number of seconds, and converts to hours, minutes, and seconds. */
// minute = 60 seconds : 60
// hour = 60 * 60 seconds : 3600
// day = 24 * 60 * 60 seconds : 86400
// year = 365 * 24 * 60 * 60 seconds : 31536000
#include <stdio.h> // from original script
/* Define constants */
#define SECS_PER_MIN 60 // from original script
#define MIN_PER_HOUR 60
#define HOURS_PER_DAY 24
#define DAYS_PER_YEAR 365
#define SECS_PER_YEAR 31536000
#define SECS_PER_HOUR 3600 // from original script
#define SECS_PER_DAY 86400
#define LIMIT 2147483647
unsigned seconds, minutes, hours, days, years, secs_remain, mins_remain, hours_remain, days_remain; // modified from original script
int main(void) // from original script
{
seconds = minutes = hours = days = years = secs_remain = mins_remain = hours_remain = days_remain = 0;
/* Input the number of seconds. */
printf( "Enter the number of seconds ( > 0, < %d ): \n", LIMIT ); // modified from original script
scanf( "%d", &seconds ); // from original script
years = seconds /SECS_PER_YEAR;
days = seconds / SECS_PER_DAY;
hours = seconds / SECS_PER_HOUR; // from original script
minutes = seconds / SECS_PER_MIN; // from original script
days_remain = days % DAYS_PER_YEAR;
hours_remain = hours % HOURS_PER_DAY;
mins_remain = minutes % MIN_PER_HOUR; // modified from original script
secs_remain = seconds % SECS_PER_MIN; // from original script
printf( "%d seconds is equal to ", seconds ); // from original script
if ( seconds < SECS_PER_HOUR )
{
printf( "%d m, and %d s\n", minutes, secs_remain );
return 0;
}
else if((seconds >= SECS_PER_HOUR ) && (seconds < SECS_PER_DAY ))
{
printf( "%d h, %d m, and %d s\n", hours, mins_remain, secs_remain ); // from original script
return 0; // from original script
}
else if((seconds >= SECS_PER_DAY ) && (seconds < SECS_PER_YEAR ))
{
printf( "%d d, %d h, %d m, and %d s\n", days, hours_remain, mins_remain, secs_remain );
return 0;
}
else if((seconds >= SECS_PER_YEAR ) && (seconds < LIMIT ))
{
printf( "%d y %d d, %d h, %d m, and %d s\n", years, days_remain, hours_remain, mins_remain, secs_remain );
return 0;
}
else if(seconds > LIMIT )
{
printf("error: %d is an excessive amount of seconds.\n", seconds);
printf("range for seconds must be between 0 and %d!\n", LIMIT );
printf("If number of seconds exceeds %d, then it is beyond range for type 'int'.\n", LIMIT );
printf("EXITING seconds program. \n");
return 1;
}
}
Thanks, I've changed all "%d"
to "%u"
and Limit.
I've also put this together for future reference:
#include <stdio.h>
#include <limits.h>
int main(void)
{
printf("\n");
printf("The number of bits in a byte =\t\t\t\t %d\n", CHAR_BIT);
printf("The maximum number of bytes in a multi-byte character =\t %d\n", MB_LEN_MAX);
printf("The minimum value of SIGNED CHAR =\t\t\t %d\n", SCHAR_MIN);
printf("The maximum value of SIGNED CHAR =\t\t\t %d\n", SCHAR_MAX);
printf("The maximum value of UNSIGNED CHAR =\t\t\t %d\n", UCHAR_MAX);
printf("The minimum value of SHORT INT =\t\t\t %d\n", SHRT_MIN);
printf("The maximum value of SHORT INT =\t\t\t %d\n", SHRT_MAX);
printf("The maximum value for an UNSIGNED SHORT INT =\t\t %u\n", USHRT_MAX);
printf("The minimum value of INT =\t\t\t\t %d\n", INT_MIN);
printf("The maximum value of INT =\t\t\t\t %d\n", INT_MAX);
printf("The maximum value for an UNSIGNED INT =\t\t\t %u\n", UINT_MAX);
printf("The minimum value of CHAR =\t\t\t\t %d\n", CHAR_MIN);
printf("The maximum value of CHAR =\t\t\t\t %d\n", CHAR_MAX);
printf("The minimum value of LONG =\t\t\t\t %ld\n", LONG_MIN);
printf("The maximum value of LONG =\t\t\t\t %ld\n", LONG_MAX);
printf("The maximum value for an UNSIGNED LONG INT =\t\t %Lu\n", ULONG_MAX);
printf("\n");
return(0);
}
回答1:
It looks like you're trying to ensure that the user doesn't type too big a value for your seconds
variable to hold. So you read a value into seconds
, and then (among other things) you try to check to see if it's too big. But the problem is, by the time you've read a too-large value into seconds
, you can't really say what it is, because the value is too big for seconds
to hold, so it's been transformed into some other value!
Now, it's a little more complicated than that, because you've declared seconds
as an unsigned int
. It's likely that the actual range of seconds
on your system is from 0 to 4294967295. So when you type in a value like 2147483648, seconds
can hold this value -- although it is greater than LIMIT
, so you do make it through to the else
clause.
But then, when you make it through to the else
clause, you print out the value of seconds
using %d
. But %d
is for signed ints, not unsigned. So that's why you're seeing those weird negative values.
I'm not sure if this answers your question, and I'm not sure what you really want your code to do (that is, whether you'd really rather be using signed or unsigned arithmetic), so I'm not providing any "fixed" code for you to try. But I hope this helps explain what's going on. If you have more questions, just ask.
Addendum: what's actually going on here is that when they overflow, unsigned integers "wrap around" from the too-high value back to 0. Also, although it's not guaranteed, signed integers tend to wrap around also, from their own too-high value back to the big, negative, too-low value.
For 32-bit integers, the values wrap around in a cycle of 4294967296. So unsigned can go from 0 to 4294967295, and if you try to jam in 4294967296 you get 0, and if you try 4294967297 you get 1, and if you try 4294967298 you get 2, etc.
When they're signed, legal values go from -2147483648 to +2147483647, and if you try to jam in +2147483648 you get -2147483648, and if you try to jam in +2147483649 you get -2147483647, etc.
So since 2147483650 is greater than 2147483647, it won't fit, and the value you do get is 2147483650 - 4294967296 = -2147483646. What about 9876543210? Way too big, and 9876543210 - 4294967296 = 5581575914 which is still too big, but 5581575914 - 4294967296 = 1286608618 which does fit. So that's why you got that weird number 1286608618 in your last example.
来源:https://stackoverflow.com/questions/42176318/unsigned-long-int-accepts-out-of-range-values