unsigned long int accepts out of range values

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-13 08:46:21

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!