Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Bug Score Multiplier won't round correctly

Discussion in 'Scripting' started by serbusfish, Aug 8, 2023.

  1. serbusfish

    serbusfish

    Joined:
    Dec 27, 2016
    Posts:
    246
    I'm having an issue with my score multiplier not rounding. I need it to round to values with no more than 2 decimel places, so 1.00, 2.52, 3.78, etc. It starts this way but once it goes above 3.50 it starts to return values like 3.59999. I thought the code I had was what was needed, is there something more or different I need to do?

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using TMPro;
    4. using UnityEngine.SceneManagement;
    5.  
    6.  
    7. public class GameController : MonoBehaviour
    8. {
    9.  
    10. public float multiValue;
    11. public float startingMulti = 1.00f;
    12. private float score;
    13. public TextMeshProUGUI Score;
    14. public TextMeshProUGUI Multiplier;
    15.  
    16. void Start()
    17.        
    18. {
    19. {
    20.  
    21. multiValue = startingMulti;
    22. UpdateScore();
    23. }
    24. }
    25.  
    26. void UpdateScore()
    27. {
    28. Score.text = "Score: " + score.ToString("0");
    29. Multiplier.text = "Multiplier: x " + System.Math.Round(multiValue, 2).ToString("f2");
    30. }
    31.  
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    3,899
    Try this:
    Code (CSharp):
    1. Multiplier.text = "Multiplier: x " + multiValue.ToString("#.##");
    If rounding the second digit up/down is not working as expected try with round first but I think the round is actually what's going to get you 1.4999997f values rather than 1.5f and it would be interesting to see whether that prints 1.50 or 1.49:
    Code (CSharp):
    1. Multiplier.text = "Multiplier: x " + Mathf.Round(multiValue, 2).ToString("#.##");
    Note that this will always output two decimal digits as in: "1.00" and "1.50". For consistency I would definitely prefer this.

    Whereas this has no meaning:
    public float startingMulti = 1.00f;

    It is equivalent to writing this:
    public float startingMulti = 1f;
     
    Last edited: Aug 8, 2023
    Yoreki likes this.
  3. serbusfish

    serbusfish

    Joined:
    Dec 27, 2016
    Posts:
    246
    Unfortunately neither of these alternatives produce a different result.

    What I will clarify is that all values under 1.5 are rounding correctly, once the number rises past 1.5 that's when I start getting 1.59999, etc.
     
  4. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,718
    Sounds like you have code somewhere that is adding
    0.1
    to your score? This is inherently going to result in issues like this due to how binary floating point numbers are stored. 0.1 is actually not a number that can be represented in binary without an infinitely repeating decimal. Similar to how 1/3 cannot be represented in base 10 without an infinite repeating decimal expansion, neither can 1/10 in base 2.

    Try it yourself here!
    https://www.h-schmidt.net/FloatConverter/IEEE754.html
    Screenshot 2023-08-08 at 3.03.01 PM.png

    If you need this kind of precision, the best option is to store your score in an integral datatype instead. Something like this:

    Code (CSharp):
    1. int tenthsOfAPoint;
    2. public float Score => tenthsOfAPoint * 10;
    3.  
    4. void AddPoints(float score) {
    5.   float tenths = score * 10;
    6.   int tenthsInt = Mathf.RoundToInt(tenths);
    7.   AddTenths(tenthsInt);
    8. }
    9.  
    10. void AddTenths(int tenthsToAdd) {
    11.   tenthsOfAPoint += tenthsToAdd;
    12. }
    13.  
    14. public string GetScoreAsString() {
    15.   string scoreStr = tenthsOfAPoint.ToString();
    16.   char lastChar = scoreStr[^1];
    17.   string firstPart = score.Substring(0, scoreStr.Length - 1);
    18.   return $"{firstPart}.{lastChar.ToString()}0";
    19. }
     
    spiney199 and Kurt-Dekker like this.
  5. ijmmai

    ijmmai

    Joined:
    Jun 9, 2023
    Posts:
    188
    Can't you just use int instead? For keeping track, for adding and subtracting. When you need to multiply you divide the result by 100.
     
    Kurt-Dekker likes this.
  6. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,718
    which is... exactly what my post right above yours was showing?
     
  7. tleylan

    tleylan

    Joined:
    Jun 17, 2020
    Posts:
    521
    It looks a bit complex to me as well. Breaking the number into an array? Is this necessary? Isn't it more or less as follows:

    Code (CSharp):
    1.  
    2. var test = new Score();
    3.      
    4. test.AddPoints(1);
    5. Console.WriteLine(test.GetScore());
    6.      
    7. test.AddPoints(5);
    8. test.AddPoints(30);
    9. Console.WriteLine(test.GetScore());
    10.    
    11. test.SetPoints(0);
    12. test.AddPoints(100);
    13. Console.WriteLine(test.GetScore());
    14.  
    Code (CSharp):
    1.  
    2.   public class Score
    3.   {
    4.     private int points;
    5.  
    6.     public void SetPoints(int setValue) {
    7.       points = setValue;
    8.     }
    9.  
    10.     public void AddPoints(int addValue) {
    11.       points += addValue;
    12.     }
    13.  
    14.     public string GetScore() {
    15.         return ((float)points / 100).ToString("F");
    16.     }  
    17.   }
    18.  
     
    ijmmai likes this.
  8. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    20,082
    He's just using the hat operator (^) to chop off everything to the right of the tenths place in the string. If you want to read up on the feature that it belongs to check the link below. Here's an alternative that uses modulo (%).
    Code (csharp):
    1. public string GetScoreAsString() {
    2.   return $"{tenthsOfAPoint / 10}.{tenthsOfAPoint % 10}0";
    3. }
    https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/ranges
     
    Last edited: Aug 9, 2023
  9. ijmmai

    ijmmai

    Joined:
    Jun 9, 2023
    Posts:
    188
    I was more thinking along the line of tleylan 's code. No need to round when adding points or substring when going to text.
     
  10. dlorre

    dlorre

    Joined:
    Apr 12, 2020
    Posts:
    700
    I tried your code in Unity 2023.2 and I don't get this error.
     
  11. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,718
    There is no array in my code. We are simply using different techniques to formatting it.
     
  12. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,718
    The rounding was simply to allow the convenience of using a float to set the score. If you don't want that, just ignore that method. I was simply giving you more options.
     
  13. ijmmai

    ijmmai

    Joined:
    Jun 9, 2023
    Posts:
    188
    I understand. It wasn't my problem to begin with though, I was just brainstorming, thinking out loud.
     
  14. tleylan

    tleylan

    Joined:
    Jun 17, 2020
    Posts:
    521
    Ah, I was unaware of that C# operator, good to know (I guess) :) I think we can agree that **if** the requirements changed and the increment or the format needed changing one solution required a lot more modification. In any case there are alternatives.
     
  15. serbusfish

    serbusfish

    Joined:
    Dec 27, 2016
    Posts:
    246
    Thank you everyone for your suggestions. Embarrassingly it turns out I had a redundant line in "Update()" that was overriding the line in "UpdateScore()" :oops: