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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Resolved [SerializeField, Range(0, int.MaxValue)] behavior, negative values

Discussion in 'Scripting' started by HoLyEmperor, Feb 27, 2023.

  1. HoLyEmperor

    HoLyEmperor

    Joined:
    Dec 13, 2022
    Posts:
    3
    Resolution:
    The RangeAttribute constructor accepts floats, like this:

    Code (CSharp):
    1. public sealed class RangeAttribute : PropertyAttribute
    2.     {
    3.         public readonly float min;
    4.         public readonly float max;
    5.  
    6.         public RangeAttribute(float min, float max)
    7.         {
    8.             this.min = min;
    9.             this.max = max;
    10.         }
    11.     }
    Thus, there's an implicit conversion from integers, causing the issue with a very large integer like int.MaxValue.

    Original post:

    I apologize in advance if this topic has been answered. A search of this forum gave a thread from 6+ years ago, which is related but unresolved:
    https://forum.unity.com/threads/range-0-int-maxvalue-limiting-to-int32-minvalue.879781/

    To preface, I solved the issue described below in a manner that works fine for my project. I will describe my solution. However, I'd like to understand what's happening as my fix seems more like a hack.

    Here's a snippet of the problem code:

    Code (CSharp):
    1. using System;
    2. using Unity.Burst;
    3. using Unity.Collections;
    4. using Unity.Jobs;
    5. using UnityEngine;
    6.  
    7. public class MyClass : MonoBehaviour
    8. {
    9.     //2147483647
    10.     [SerializeField, Range(0, int.MaxValue)]
    11.     int seed = 0;
    12.  
    13.     // ...
    14.  
    15.     void OnValidate() {
    16.         print(seed);
    17.     // ... }
    18. }
    The Inspector slider now goes from 0 to -2147483648. Negative. A print(int.MaxValue) confirms that int.MaxValue == 2147483647. Positive as it should be. The negative value is actually int.MinValue. Why the negative values?

    I tried hard coding 2147483647 instead of using int.MaxValue:

     [SerializeField, Range(0, 2147483647)] 
    But this clamps the Inspector at -2147483520, somehow.

    The hacky fix is simple, with a subtraction:

    [SerializeField, Range(0, int.MaxValue - 64)]
    This works for my project. I only wanted the Inspector slider for some testing and debugging. The rest of this is an exploration of the Inspector issue.


    Nothing smaller than 64 works; anything equal or greater is fine. The number 64 seems very specific. In binary that looks like:

    0111 1111 1111 1111 1111 1111 1111 1111 //2147483647

    to

    0111 1111 1111 1111 1111 1111 1011 1111 //2147483647 - 64

    However, I have no idea why specifically 64. What's happening with that bit?

    Attempting to narrow down the problem, I tried a number of things:

    Code (CSharp):
    1. [Range(0, int.MaxValue)] public int seed = 0;  //note: not generally equivalent, but ok in my project.  This also gives negative values unless I subtract 64 or greater.
    2.  
    3. [SerializeField, Range(0, (-1)*int.MaxValue)] int seed = 0; //still gives negative values, but this time it should
    4.  
    5. //You might ask, why not try uint?  That creates a new wrinkle
    6. [SerializeField, Range(0, uint.MaxValue)] //now Inspector is clamped at 0, somehow.
    7.  
    8. //You might also ask, why exclude the negative integers?  No reason!  But it gets even stranger:
    9. [SerializeField, Range(int.MinValue, int.MaxValue)] //inspector is clamped to int.MinValue
    10. //using subtractions < 64 will also give unexpected behavior
    11.  
    12. //but
    13. [SerializeField, Range(int.MinValue, int.MaxValue - 64)]  //works fine!
    I tried a number of other things not documented here.

    tl;dr: [SerializeField, Range(0, int.MaxValue)] behaves weird.
    [SerializeField, Range(0, int.MaxValue - 64)] does not behave weird.

    What the heck is going on?
     
    Last edited: Feb 27, 2023
  2. chemicalcrux

    chemicalcrux

    Joined:
    Mar 16, 2017
    Posts:
    717
    My random guess: Range is using floats, not ints, so the int -> float conversion is resulting in significant error.

    upload_2023-2-26_21-41-58.png

    Subtracting 64 is enough to avoid this.

    upload_2023-2-26_21-42-28.png
     
    mopthrow and HoLyEmperor like this.
  3. HoLyEmperor

    HoLyEmperor

    Joined:
    Dec 13, 2022
    Posts:
    3
    Interesting! I'm guessing that is the issue, as it corresponds to the values I'm seeing. However, https://docs.unity3d.com/ScriptReference/RangeAttribute.html

    Code (CSharp):
    1. public class Example : MonoBehaviour
    2. {
    3.     // This integer will be shown as a slider,
    4.     // with the range of 1 to 6 in the Inspector
    5.     [Range(1, 6)]
    6.     public int integerRange;
    7.  
    8.     // This float will be shown as a slider,
    9.     // with the range of 0.2f to 0.8f in the Inspector
    10.     [Range(0.2f, 0.8f)]
    11.     public float floatRange;
    12. }
    suggests that it should work as either ints or floats? Perhaps the documentation is outdated or incorrect?

    eta: Also, why is 64 specifically the magic number? Just a coincidental power of 2?
     
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    6,015
    Integers can be implicitly converted to float values. If you check the constructor, the constructor only takes two float values (of which you use integers because of the implicit conversion).

    So @chemicalcrux is correct. int.MaxValue is going to cause errors when converted to a float.
     
    HoLyEmperor likes this.
  5. HoLyEmperor

    HoLyEmperor

    Joined:
    Dec 13, 2022
    Posts:
    3
    Indeed, there it is!

    Thanks, and to @chemicalcrux as well. I didn't expect to see an implicit conversion there. Now I know that can happen even in a declaration. Updated OP.
     
  6. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,572
    You can of course create your own attribute and a corresponding PropertyDrawer yourself that specifically works with ints. Though you have to find a descriptive name for it. Like
    RangeInt
    :)
     
  7. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    6,015
    chemicalcrux and Bunny83 like this.