Mathf.Round sometimes buggs on me?

Hi,

Sorry about posting this here, seems like an easy one but I swear to god I’m going crazy.

In one of my test-projects I have the following code:

    public void clickedFunction()
    {
        timeStopper = timeRemaining;
        timeStopperCalculate = Mathf.Round(timeStopper * 100f) * 0.01f;
        timerField.text = timeStopperCalculate.ToString();
        Debug.Log(timeStopperCalculate);
    }

Works great, I’m happy but I have a bug when I test it. When I click then sometimes (i’d say 2-3% of the time) it wont actually round to two decimals instead I get this:

Anyone willing to jump in and tell me what I’m messing up?:slight_smile:

//Murten

I don’t think you’re “messing up” anything. This is likely just floating point imprecision. In a base 2 numbering system it’s not possible to precisely represent decimal 0.1

Here’s more:

https://starmanta.gitbooks.io/unitytipsredux/content/floating-point.html

You can avoid all that rounding noise by simply telling C# to format it for you. Look up floating point formatting, as supplied in an argument to ToString()

First off all, thank you for the quick answer.

I think I’m getting the point but will need to read it a couple of times. I haven’t been doing any Unity training for years so could I maybe just ask you two questions.

Is it possible to just truncate/cut off a float to two decimal points? Like in the example below? But to a float variable

timerField.text = timeRemaining.ToString("F2");

Would a way of doing it be to float.parse that text back into a float afterwards?
(Nvm it seems you need to be specific about the placement of the numbers and since Im counting from 10< to 0 there will be a problem.)

Im probably talking gibberish but you know, gotta try:)

You can truncate to 2 decimal places using ToString, yes, because you’re converting it to a string which isn’t really a numeric format.

But if you try to put it back into a float… not necessarily.

What is “2 decimal places” in a float?

Note a float is binary! And the word “decimal” in “decimal places” implies well… decimal. Or base 10.

2 decimal places doesn’t exist in binary!

Which is the root of the entire problem. You can’t accurately represent decimal fractional values in binary all the time. Values like 0.1 in decimal are repeating values in binary. Think how 1/3 is repeating in decimal, with 1/10 is repeating in binary. Specifically it’s 0.0001100110011001100110011… and since float only gets 32 bits to store everything, well you end up chopping off the end resulting in a value just shy of 1/10… hence your 8.099999 in your OP.

In the end this usually isn’t an issue since games don’t really actually need to be that accurate. You just want to clean it up for display. Hence why the format strings let you truncate to exactly the decimal places you want and they’ll round for you and all that good stuff.

There are times where it is critical. For example finances.

This is why the ‘decimal’ type exists (sometimes called the ‘money’ type). It doesn’t store in binary. It stores in scientific notation of base 10. This gives the base 10 decimal behaviour you expect as a numeric data type.
https://docs.microsoft.com/en-us/dotnet/api/system.decimal?view=net-6.0

But this comes at the expense of being slower, as well as fatter (it’s 128-bit). You really should only use this when needing to do decimal arithmetic where decimal precision is paramount… such as banking/finances.

Hey,

Thanks for the answer, I’m with you but maybe I should just explain what I’m trying to do.

What I’m trying to do is just a very simple game for my brothers birthday, it’s just about guessing how many seconds has passed.

So if the game Randoms 17 seconds I want it to count down from that without showing the countdown and then he clicks a button and then it get saved to a float for Player1, then the next person does the same and it’s saved to Player2 etc.

So the reason I only want TWO decimals and not more is because there is no reason but it could be good to have two decimals if it gets close. It’s just so strange to me that I run a math random and every now and then it just saved a float with all the decimals.

When four players has done it then I want the time they clicked to be displayed on the screen and then I don’t want one of them to go nuts and have a lot of decimals.

What I could do is that I could :

timerField.text = timeRemaining.ToString("F2");

When they click and so it’s already stored and I can show on the screen and then I could run the calculation of whos closer in the background without them knowing it looks like cray, but I got to admit even though its a simple game it’s annoying that it could look different in the floats.
PS. Yes, my brother thinks he is gods gift to man when it comes to guessing seconds passed so I wanna put him in his place a bit.

Yes. This is what you do.

You don’t care that it’s lots of decimals inside the numeric type.

You just care that when you DISPLAY it, that it is truncated to 2 decimals.

Which is exactly what a format string does. Like in your example. That will work to display what you want.

Just don’t expect the float to actually store that value. It won’t. But that doesn’t matter.

I truly appreciate your help, I will be back to these forums when I can actually help people, hopefully soon!