为什么这个数字停滞不前了?

Why the number stuck and doesn't increase anymore?

首先,我是编程新手。 这对我来说只是一个简单有趣的项目,但我遇到了意想不到的问题,我不知道为什么。

背景:有点像《土拨鼠之日》,从一个月开始循环,到下一个循环时间减少一秒。我正在尝试计算总年数(通过计算总秒数)。 这是我的代码:

#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;
}

问题:在我的代码中,在大约循环 2461000 中,Total Seconds 被卡住了,不再增加。 奇怪的是,卡住的数字是3359234850816,距离long类型的最大数字还差得很远。 而且,如果我尝试正常计算(在另一个简单的算术代码中),就不会像那样卡住。

有人可以帮忙吗? 此外,我愿意接受任何关于整理它的建议,或任何与此相关的建议。谢谢

您已经超出了所选数字表示的限制。

这可能令人惊讶,因为大多数时候,当混合数字类型时,C++ 将转换为最能表示结果的类型。例如,将intlong long相加计算为long long。添加一个floatdouble计算为一个double。但是,您通过添加整数类型和 float.

选择了一条奇怪的路线

usual arithmetic conversions,将任意整数类型和一个float相加计算为一个float。任何整数类型。如果您碰巧有一个 128 位 long long 和一个 32 位 float,将它们相加会产生一个 32 位 floatfloat 被视为可以更好地表示数字,这是有好处的。典型的 32 位 float 确实具有与 128 位整数大致相同的 运行ge,具有能够表示非整数的好处。然而,虽然这种想法可能落后于规则,但规则只是 float 与整数类型组合产生 float.

将此应用于更新 totalSeconds.

的行
totalSeconds = totalSeconds+secondsInLoop;

虽然 totalSecondslong,但 secondsInLoopfloat。这意味着当这些相加时,结果是 float。据推测,这意味着一个 32 位值在有效数中有 24 个逻辑位(23 个实数位加上假定的 1)。这是结果的精度。仅保留总和的最高有效 24 位。当你的值停止增加时,整数是 3359234850816 而浮点值是 131071 (我 运行 你的代码想出这个)。这些值的比例是25629124,一个25位的值。与totalSeconds相比,secondsInLoop的价值如此之小,就float加法而言,还不如为零呢

要获得正确的计算,您需要更改结果的类型。最直接的方法是对所有整数值使用 long ,只记得在除法之前转换为浮点数。另一种方法是将 secondsInLoop 更改为 double(就像您的其他保存整数的浮点变量一样 - 奇怪的是您选择了这个)。由于典型的 double 具有 53 位逻辑精度,因此它可以精确表示所有 32 位整数。因此,避免了这个问题(直到您需要转到 64 位整数...)。您还可以在总和中转换为整数类型,这会覆盖到浮点数的转换。

所以

long secondsInLoop = 86400*30;

double secondsInLoop = 86400*30;

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

可以更精确地表示您的总和。