0

first of all, i'm new in programming. This is just a simple fun project for me, but i met unexpectedly problem and i don't know why.

Background: This is something like "Groundhog Day", start loop from a month, by next loop the time decrease by one second. I'm trying to calculate the total years (by counting the total seconds). Here is my code:

#include <iostream>
#include <iomanip> 
using namespace std;

int main() {
    int loop=1;
    double secondsInDay = 60*60*24;     //86400
    double secondsInMonth = 86400*30;   //2592000
    float secondsInLoop = 86400*30;
    double totalYears = 0;
    long totalSeconds = 0;
    
    cout <<"Loop : "<<loop<<endl;
    totalSeconds += secondsInLoop;
    cout <<"-> Total seconds: "<<totalSeconds<<endl;

    cout <<fixed<<setprecision(0)<<"-->Total time in this Loop: "<<secondsInLoop;
    cout <<fixed<<setprecision(3)<<" seconds / "<<secondsInLoop/60<<" minutes / "<<secondsInLoop/3600<<" hours / "<<secondsInLoop/86400<<" days"<<endl<<endl;

    while (secondsInLoop>0)
    {
        if (loop%1000==0)
        {
            cout <<"Loop: "<<loop<<endl;
            cout <<"-> Total Seconds: "<<totalSeconds<<endl;

            cout <<fixed<<setprecision(0)<<"-->Total time in this Loop: "<<secondsInLoop;
            cout <<fixed<<setprecision(3)<<" seconds / "<<secondsInLoop/60<<" minutes / "<<secondsInLoop/3600<<" hours / "<<secondsInLoop/86400<<" days"<<endl;

            totalYears = totalSeconds/(secondsInMonth*12); //31104000
            cout <<fixed<<setprecision(3)<<"-->> Total Years: "<<totalYears<<endl<<endl;
        }
        totalSeconds = totalSeconds+secondsInLoop;
        secondsInLoop--;
        loop++;
    }
    
    cout <<"Loop: "<<loop<<endl;
    cout <<"#-> Total Seconds: "<<totalSeconds<<endl;

    totalYears = totalSeconds/(secondsInMonth*12); //31104000
    cout <<"#->> Total Years: "<<totalYears<<endl<<endl;

    return 0;
}

Problem: In my code, in about loop 2461000, the Total Seconds is stuck and doesn't increase anymore. It's weird because the stuck number is 3359234850816, and it's far from the max number of long type. And, if i try to calculate normally (in another simple arithmetic code) there is no stuck like that.

Can anyone help please? Also, i'm open to any suggestion for tidy it up, or anything for the matter. Thanks

  • What happens if you do `totalSeconds += static_cast(secondsInLoop);` instead? – Ted Lyngmo Dec 27 '21 at 03:25
  • @JaMiT For the first, the precision doesn't effect the outcome, just for the display. For the second, yeah, i try to change the value of "secondsInLoop" to int, and HELL IT WORKS. Whew it turn out to be so simple. I'm don't really remember why i'm using float type tbh. It may be a "residue" from first start this code and using another method. THANKS – Prabu Farhan Dec 27 '21 at 03:30
  • @Ted Lyngmo It's also works, even the type is still the float type, and the results is the same. i'll have to look it up what it actually do. Thanks – Prabu Farhan Dec 27 '21 at 03:32
  • 1
    What do you believe "the max number of long type" is? – Sam Varshavchik Dec 27 '21 at 03:46
  • @PrabuFarhan *"For the first, the precision doesn't effect the outcome, just for the display."* -- Exactly as I intended. I was not trying to answer your question in the comments. I was suggesting a way to put more information in the question. You told us what the value of `totalSeconds` is when it gets stuck, but not the value of `secondsInLoop`. – JaMiT Dec 27 '21 at 03:47

1 Answers1

2

You've surpassed the limits of your chosen numeric representation.

This might be surprising since most of the time, when mixing numeric types, C++ will convert to the type that can best represent the result. For example, adding an int and long long is calculated as a long long. Adding a float and double is calculated as a double. However, you've chosen one of the odd routes by adding an integer type and a float.

By the usual arithmetic conversions, adding any integer type and a float is calculated as a float. Any integer type. If you happen to have a 128-bit long long and a 32-bit float, adding them produces a 32-bit float. The float is treated as if it can better represent numbers, which has merit. A typical 32-bit float does have about the same range as a 128-bit integer, with the benefit of being able to represent non-integers. However, while this thinking might be behind the rule, the rule is simply that a float combined with an integer type produces a float.

Apply this to the line that updates totalSeconds.

totalSeconds = totalSeconds+secondsInLoop;

While totalSeconds is a long, secondsInLoop is a float. That means that when these are added, the result is a float. Presumably, this means a 32-bit value with 24 logical bits in the significand (23 real bits plus an assumed 1). This is the precision of the result. Only the most significant 24 bits of the sum are preserved. When your value stops increasing, the integer is 3359234850816 and the floating point value is 131071 (I ran your code to come up with this). The ratio of these values is 25629124, a 25-bit value. Compared to totalSeconds, the value of secondsInLoop is so small, it might as well be zero as far as float addition is concerned.

To get the correct calculation, you need to change the type of the result. The most straight-forward approach is to use long for all of your integer values, and just remember to cast to floating point before dividing. Another approach would be to change secondsInLoop to be a double (like your other floating point variables that hold integers—odd that you singled this one out). Since the typical double has 53 logical bits of precision, it can represent all 32-bit integers precisely. Thus, this problem is avoided (until you need to go to 64-bit integers...). You could also cast to an integer type in the sum, which overrides the conversion to floating point.

So

long secondsInLoop = 86400*30;

or

double secondsInLoop = 86400*30;

or

totalSeconds = totalSeconds + static_cast<long>(secondsInLoop);

will allow a more precise representation for your sum.

JaMiT
  • 14,422
  • 4
  • 15
  • 31