- Home /
Fixing a float value's round error? [Solved]
I'm messing about with a 'clicker' type game, and have previously used InvokeRepeating once per second to add a number of whole (int) units once per second. I wanted to change the frequency of updates to .1 seconds, so I call InvokeRepeating every .1 second, and divide autoUnits (units per second) by 10 when it's being called to update the number of units.
I want units to only display as an int, but i had to change it to a float in order for the math to work. My problem now is that i'll get rounding errors which will add .999999 units sometimes as opposed to 1. How can I fix this?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ClickCounter : MonoBehaviour {
public float units = 0;
public int autoUnits = 0;
public Text countText;
public Text autoText;
// Use this for initialization
void Start ()
{
SetCount();
InvokeRepeating("AutoAdd", 0f, .1f);
}
// Update is called once per frame
void Update ()
{
SetCount();
}
public void OnClick()
{
units += 1;
}
public void AddOneClick()
{
ClickDefiner(1);
}
public void AddTenClicks()
{
ClickDefiner(10);
}
public void AddHundredClicks()
{
ClickDefiner(100);
}
private void ClickDefiner(int tempUnit)
{
autoUnits += tempUnit;
}
private void AutoAdd()
{
units += (autoUnits/10);
}
private void SetCount()
{
countText.text = "Units: " + units.ToString();
autoText.text = "U/sec: " + autoUnits.ToString();
}
}
Answer by $$anonymous$$ · Aug 26, 2018 at 04:54 PM
I went back and added a rounding function in update which is read by SetCount instead of the actual unit value, which seems to have worked, but is there a more elegant way to do this?
private void RoundUnits()
{
roundedUnits = Mathf.RoundToInt(units);
}
private void SetCount()
{
countText.text = "Units: " + roundedUnits.ToString();
autoText.text = "U/sec: " + autoUnits.ToString();
}
Also, to note, this required me changing autoUnits to a float value to work.
Answer by JVene · Aug 26, 2018 at 05:13 PM
You've asked if there's a more elegant way to deal with this. Well, maybe...not :) (lol)
This is a long standing issue, and goes all the way down to the CPU itself in the representation of floating point values, which is to say this isn't a language issue, or something unique or unknown. It's just the way floating point values are managed (search for IEEE floating point, mantissa and exponent).
I've read long running threads from highly educated people arguing these points. One argument, running at least 15 pages of text over several posts in a programming forum, just didn't seem to understand. His viewpoint was basically that math is what it is and the computer should conform, not the other way around. His problem, the computer is what it is, and it can't be made to conform to preconceptions which conflict with reality.
There is no way around the fact that a floating point number, as represented in all modern CPU's, and therefore reflected by extension on most any language or software tools, is an approximation. It is not an exact representation. Nothing will ever fix that.
No matter what elegance one applies to a solution, it will be mere syntactic sugar. The bottom line is that in order to convert cleanly to an integer, a floating point number will have to be run through a 5/4 rounding algorithm (unless truncation were acceptable, which it isn't in your case). Basically, you select a precision (which, by int, is given at zero decimal places). You add half of that precision, which is to say you add 0.5 in your case), then truncate - simply convert to an int.
You can use Mathf, you can call round or ceil or floor or whatever. You can cast to an integer after adding 0.5f. Bottom line, they all do something very similar under the hood. They'll truncate to convert, and if truncation follows the increment by 0.5f, it is 5/4 rounding.
For syntactical sugar, one could form some specialized struct or class that behaves like a float but automatically returns 'normalized' integers to you, which may allow more elegant writing, but it is the same thing under the hood.
I remember this from my early study in the late 70's, and I've read from history that it dates back to the very first formation of the floating point format which is now known by the IEEE standards on most CPU's.
Would using doubles resolve this or would it be more of the same in a different flavor?
It only changes the precision. Doubles are still a mantissa and exponent representing a floating point value. The problem is otherwise identical, though the specifics change slightly (not enough to be of value to you).
Answer by Em3rgency · Aug 26, 2018 at 04:41 PM
Is this what you want?
private void AutoAdd()
{
units += Mathf.CeilToInt(autoUnits/10);
}
Not entirely. If autoUnits is 1, then it will divide by ten and set it right back to 1, which will end up outputting 10 units per second when called.
But then when do you get 0.9999 ins$$anonymous$$d of 1? Are you saying that when autoUnits is 10, 10/10 is giving you 0.9999?
In the end result, units. AutoAdd is called 10x a second by InvokeRepeating, which adds the autoUnits to units. So when autoUnits is 10, it adds 1 unit every tenth of a second, but when autoUnits is 1, its adding .1 units every tenth of a second, which was introducing some error.
Answer by elenzil · Aug 26, 2018 at 11:40 PM
track it as a float and round to int only when you display it.
Your answer
Follow this Question
Related Questions
Looking at a target in 2D 0 Answers
[MATH] Vectors, magnitudes, and bears; Oh My! -Solved 2 Answers
Quick Math Help with Normalized numbers 2 Answers
Unity thinks that 14 = 15? 1 Answer
Move an object to a specific distance 0 Answers