Decreasing float goes faster if the value is too large?

I’m using unity UI to make an android app, a very simple one. You can choose how many minutes and hours you want, and it turns them into a seconds-countdown.
It works pretty well, however when the sum of hours and minutes (the seconds) is too large, the countdown is a lot faster than it should be.


using UnityEngine;
using UnityEngine.UI;

public class MainCode : MonoBehaviour
{
    private float seconds;
    private float textSeconds;
    private float minz;
    private float hourz;

    public Text timeText;
    public Text minutesText;
    public Text hoursText;

    private bool countDown = false;
    private bool countUp = false;
    private bool canSlider = false;

    public Slider slider;

    public GameObject pauseSign;
    private bool pauseSigner = false;

    void Update()
    {
        timeText.text = textSeconds.ToString();
        minutesText.text = minz.ToString();
        hoursText.text = hourz.ToString();

        textSeconds = Mathf.RoundToInt(seconds);
        if (canSlider) { slider.value = seconds; }
        if (seconds <= 0f) { seconds = 0f; }

        if (minz == 0) { minutesText.text = " Minutes . . ."; minutesText.alignment = TextAnchor.MiddleCenter; }
        if (hourz == 0) { hoursText.text = " Hours . . ."; hoursText.alignment = TextAnchor.MiddleCenter; }
        if (countDown) { seconds -= Time.deltaTime; }
        if (countUp) { seconds += Time.deltaTime; }
        if (pauseSigner)
        {
            pauseSign.SetActive(true);
        } else { pauseSign.SetActive(false); }
    }
    void Awake()
    {
        Application.runInBackground = true;
    }

    public void Plus60()
    {
        if (minz <= 999f)
        {
            seconds += 60f;
            minz += 1f;
        }
    }
    public void Minus60()
    {
        if (minz > 0)
        {
            seconds -= 60f;
            minz -= 1f;
        }
    }

    public void Plus3600()
    {
        if (hourz <= 999f)
        {
            seconds += 3600f;
            hourz += 1f;
        }
    }
    public void Minus3600()
    {
        if (hourz > 0)
        {
            seconds -= 3600f;
            hourz -= 1f;
        }
    }

    public void Startz()
    {
        if (seconds > 0f)
        {
            GameObject.Find("Panel").GetComponent<Animator>().Play("StartClick");
            countDown = true;
            canSlider = true;
            slider.maxValue = seconds;
        }
        if (seconds <= 0f)
        {
            GameObject.Find("Panel").GetComponent<Animator>().Play("StartClick");
            countUp = true;
        }
    }
    public void Pausez()
    {
        pauseSigner = !pauseSigner;
        countDown = !countDown;
    }
    public void Restartz()
    {
        GameObject.Find("Panel").GetComponent<Animator>().Play("RestartClick");
        countDown = false;
        countUp = false;
        canSlider = false;
        pauseSigner = false;
        slider.value = 0f;
        slider.maxValue = 0f;
        seconds = 0f;
        minz = 0f;
        hourz = 0f;
    }
}

I’ve gone over it a few times, and I couldn’t find any errors. (It’s very simple and not so well done, I’m kinda new to coding).

Well, using a float for large second values is not really recommended. Have a look at my answer over here for a bit more insight. As you can see in the table I’ve posted the minimum difference between two numbers when they are in a certain range will go up as the range goes up. This is an inherent property of floating point numbers. The more bits you need before the decimal / binary point the less bits you have behind it.

When it comes to timers it’s usually best to either split the timer up into a decimal part and a fractional part, or directly use one of the data types that are meant for time values like TimeSpan struct and / or DateTime.

Seperating the whole numbers from the fractional part is pretty simple. You just have a seconds variable which could be a long (but just using an int is probably more than enough ^^) and in addition you have a float variable “frac” which holds the fractional part. Floating point numbers have the highest precision in the range 0 - 1 so this is perfect. All we have to do is to “carry” over to the seconds once frac goes above 1.0 or below -1.0

if (frac > 1.0f || frac < -1.0f)
{
    seconds += (int)frac;
    frac -= (int)frac;
}

Of course you would only apply deltaTime to frac for either counting up or down. The piece of code above will take care of updating the “seconds” variable which, as mentioned above, should be an integer type

I’d like to add some more notes here: First of all using multiple seperate variable to keep track of the same information is generally a bad idea. Your methods to increase / decrease your minutes / hours currently simultaneously modify the seconds as well as the hours or minutes variable. Since your seconds variable is also modified through your count down / up the variables will get out of sync at this point. So I highly recommend you choose one variable / set of variables to keep track of the value and calculate the others from that value. So to display the hours when given the time as an absolute seconds value, you just have to divide by 3600. To get the minutes you just divide the seconds by 60 but in addition you take the modulo 60 or the result.

int hours = seconds / 3600;
int minutes = (seconds / 60) % 60;
inf sec = seconds % 60;

Those would give you the hours, minutes and seconds of the timespan that is represented by our seconds variable. For example a value of 4321 would give you 1 hour 12 minutes and 1 sec. Of course if you have even longer periods you might want to add a “mod 24” to the hours and add a “days” variable where you divide the seconds by 86400 (3600 * 24).

Another important note is you said this should be an android app. Please keep in mind that Application.runInBackground has no effect on iOS or Android. That’s because apps in the background do not run anymore and can get killed by the OS at any time. If you want to have something going on in the background on Android you have to implement a native background service in Java.

Note that many games which have some sort of countdown (to recover lives or something) usually either use the system clock to figure out how much time has passed since the last time the app was running, or they do that check on their server.

So if you just want a timer that the user can start and then close the app you can just record the current time when the app was closed / paused. When the user opens the app again you can use that saved timestamp to figure out how much time has passed. Since we also should store the current state of our timer when the app closes we can restore the timer and adjust it accordingly. Of course a pure system time based approach can not produce an “alarm” or perform an action when the timer elapsed since the app isn’t running. Such things are only possible through the use of background services.

Depending on your needs Unity has now a simple interface for scheduled android notifications. I never used it since the last time I actually created an android build is about 6 years ago ^^

Just to make that clear: Your timer would work just find as long as it stays in the foreground and the user doesn’t switch to a different app.