-1

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);
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
Vasqi
  • 125
  • 9
  • 1
    And the question is? – alk Feb 11 '17 at 13:20
  • 2
    The range of unsigned long int isn't that, unless you have a very strange 31bit architecture. – Sami Kuhmonen Feb 11 '17 at 13:21
  • Are you perhaps on a 64-bit system? Then GCC will use 64 bits for `long`. – Some programmer dude Feb 11 '17 at 13:21
  • 1
    Why not read the manual page for `scanf` and note the return value along with what %d means – Ed Heal Feb 11 '17 at 13:22
  • @ some programmer dude, yes. I'm on a 2009 MacBook running Arch Linux OS. Are you saying that the range of "unsigned long int" will be different on my system? – Vasqi Feb 11 '17 at 13:25
  • 2
    By the way, I don't *see* you using `unsigned long` anywhere in the code you show? All your variables are of type `unsigned` which implies `unsigned int`, which is 32 bits on all modern platforms since the last 30 years or so. – Some programmer dude Feb 11 '17 at 13:25
  • If such monster declarations for tons of variables are taught by the book: get a better one. There are other indicators of bad style, too. – too honest for this site Feb 11 '17 at 13:26
  • Why have you chosen your variables to be global? – Ed Heal Feb 11 '17 at 13:27
  • @Ed Heal, the variables were declared globally in the book. I was simply copying it as I found it. – Vasqi Feb 11 '17 at 13:48
  • Why not use the macros privided for the ranges? Learn about `limits.h`. Don't use arbitrary values and home-made macros if avoidable. If your book did not tell you, get a better book. – too honest for this site Feb 11 '17 at 14:05
  • @Olaf, thanks. I haven't learned about macros or looked into limits.h yet. But I will have a look at that. – Vasqi Feb 11 '17 at 14:11
  • @Vasqi: Such things are a good way to judge the quality of a programming bokk, especially for C, which has a lot of pitfalls. Another one: if your book did not warn about _undefined behaviour_, instantly throw it out of the window. – too honest for this site Feb 11 '17 at 14:16

1 Answers1

3

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.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • Sir , i need to chat with you ? – Suraj Jain Feb 11 '17 at 13:43
  • Can you tell me answer to this question http://stackoverflow.com/questions/42162536/what-happens-if-i-input-number-more-than-the-range-of-data-type-in-c?noredirect=1&lq=1 – Suraj Jain Feb 11 '17 at 13:47
  • @Steve Summit, Thanks, you just pinned it down. I changed all "%d" to "%u" and then changed LIMIT = "4294967295" and the problem was solved instantly. – Vasqi Feb 11 '17 at 13:55
  • @Vasqi What happened for those things ? – Suraj Jain Feb 11 '17 at 13:58
  • @Vasqi Also read C Programming : A Modern Approach Edition 2 instead – Suraj Jain Feb 11 '17 at 13:59
  • @Suraj Jain, I took a look at that question, but my rep is too low to answer questions at the moment. I would've responded what I just learned: that the "limit for Unsigned int is 4294967295." But thanks, I will try to grab that other book. I've stumbled across a number of errors in this one that I currently am working from. – Vasqi Feb 11 '17 at 14:06
  • @SurajJain I can't answer that other question (I don't even understand it), but see if the addendum I just added to my answer gives you any insight. – Steve Summit Feb 11 '17 at 14:08
  • @Vasqi "what I just learned: that the 'limit for Unsigned int is 4294967295.'" - **You learned wrong**, that's not what the answer says! Use `limits.h` and informa about inteeger limits in C! There is no guaranteed upper limit what an integer can take. – too honest for this site Feb 11 '17 at 14:09
  • UINT_MAX 4294967295 Defines the maximum value for an unsigned int. https://www.tutorialspoint.com/c_standard_library/limits_h.htm – Vasqi Feb 11 '17 at 14:27
  • 1
    @Vasqi It defines the maximum value ***on your system***. On other systems it might be 65535, or perhaps 18446744073709551615. – Steve Summit Feb 11 '17 at 14:29
  • @SteveSummit I want to know if i input number more than the range like for example say unsigned int range is 4,294,967,295 , then if i try to store through scanf("%u" , &a) ; i input number 4,294,967,299 what will happen ? – Suraj Jain Feb 11 '17 at 15:00
  • ok i read your answer , i want to be sure , about what i read here http://stackoverflow.com/a/32648957/5473170 – Suraj Jain Feb 11 '17 at 15:06
  • @Steve Summit, understood. I took Olaf's advice and set LIMIT = UINT_MAX – Vasqi Feb 11 '17 at 15:51
  • @SurajJain I honestly don't know what would happen if you tried to input 4294967299 through `scanf %u`. Working with numbers that are too large to work with is, by definition, tricky. Me, I would never use `scanf`; I would use `strtol`, `strtoul`, `strtoll`, or `strtoull` instead, because they give more control and have better-defined behavior on overflow. If that other question doesn't answer your question, I would recommend asking a new question, rather than asking here in the comments like this. – Steve Summit Feb 11 '17 at 18:20
  • @Vasqi One final comment: if `seconds` is an `unsigned int`, the condition `if(seconds > UINT_MAX)` will *never* be true! By definition, any `unsigned int` value is always, always, always less than or equal to `UINT_MAX`. There's no way for it to hold a larger value. – Steve Summit Feb 11 '17 at 18:58
  • @Steve Summit, yeah I know. I only put that in there as a temporary experiment, so I could see what would happen if things went wrong. It no longer serves any purpose. – Vasqi Feb 11 '17 at 19:33
  • Ok sir ,thank you. Working With Things that exhibit undefined behaviour is pointless – Suraj Jain Feb 12 '17 at 01:54
  • @Vasqi You get another book for c By K.n. king 2nd edition , it would be good, a good book is a must. – Suraj Jain Feb 12 '17 at 01:55
  • @SurajJain, I found that book over on Amazon with good reviews. However, a glance at the cover seems to indicate that it only covers C89 and C99. Am I understanding that correctly, that it does not cover C11? It seems like I should be studying C11. – Vasqi Feb 13 '17 at 01:24
  • @Vasqi http://stackoverflow.com/questions/38405260/difference-between-c99-and-c11 – Suraj Jain Feb 13 '17 at 03:21
  • @Vasqi You can always update , but thing is that book is gem and fundamental concept will be cleared. – Suraj Jain Feb 13 '17 at 03:46