.NET NumericUpDown



  • <font size="2">It’s not that I hate Microsoft or anything... But I keep running in to strange things inside their code.</font>

    <font size="2">Before I continue, I just want to say to all of you how much fun Reflector can be ;-).</font>

    <font size="2"> </font>

    <font size="2">Possible thread sync errors, overflows and strange names these are the most common errors I run into in the .NET Framework.</font>

    <font size="2">This is just one example and thought I should post it.</font>

    <font size="2"> </font>

    <font size="2">Look at the code below. Nothing seems to be wrong right? This is an example of one of these strange overflows. When you enter decimal.MinValue and decimal.MaxValue into the minimum and maximum for a numeric control the overflow occurres, what happens is that it tries to calculate the amount of numbers required to display the string. Creating a decimal that contains 28 numbers that represent highest single digit (in this case 28 9’s) which results in an overflow.</font>

     <font size="2">public class NumericUpDown : UpDownBase, ISupportInitialize</font>

    <font size="2">{</font>

    <font size="2">    /* ... */</font>

    <font size="2"> </font>

    <font size="2">    internal override Size GetPreferredSizeCore(Size proposedConstraints)</font>

    <font size="2">    {</font>

    <font size="2">        decimal num5;</font>

    <font size="2">        int preferredHeight = base.PreferredHeight;</font>

    <font size="2">        int end = this.Hexadecimal ? 0x10 : 10;</font>

    <font size="2">        int largestDigit = this.GetLargestDigit(0, end);</font>

    <font size="2">        int num4 = (int)Math.Floor(Math.Log(Math.Max(-((double)this.Minimum), (double)this.Maximum), (double)end));</font>

    <font size="2">        if ((largestDigit != 0) || (num4 == 1))</font>

    <font size="2">        {</font>

    <font size="2">            num5 = largestDigit;</font>

    <font size="2">        }</font>

    <font size="2">        else</font>

    <font size="2">        {</font>

    <font size="2">            num5 = this.GetLargestDigit(1, end);</font>

    <font size="2">        }</font>

    <font size="2">        for (int i = 0; i < num4; i++)</font>

    <font size="2">        {</font>

    <font size="2">            num5 = (num5 * end) + largestDigit;</font>

    <font size="2">        }</font>

    <font size="2">        int width = TextRenderer.MeasureText(this.GetNumberText(num5), this.Font).Width;</font>

    <font size="2">        int num8 = base.SizeFromClientSize(width, preferredHeight).Width + base.upDownButtons.Width;</font>

    <font size="2">        return (new Size(num8, preferredHeight) + this.Padding.Size);</font>

    <font size="2">    }</font>

    <font size="2"> </font>

    <font size="2">    /* ... */</font>

    <font size="2">}</font>

      <font size="2">
    </font>


    <font size="2"></font>


    <font size="2"></font>


    <font size="2"></font>


    <font size="2"></font>

    <font size="2">
    </font>

    <font size="2">Run this code below to see what happens:</font>

    <font size="2"> </font>

    <font size="2">using System;</font>

    <font size="2">using System.Windows.Forms;</font>

    <font size="2"> </font>

    <font size="2">public class WtfOverflow : Form</font>

    <font size="2">{</font>

    <font size="2">  public WtfOverflow()</font>

    <font size="2">  {</font>

    <font size="2">      decimal num5;</font>

    <font size="2">      int end = 10; //this.Hexadecimal ? 0x10 : 10;</font>

    <font size="2">      int largestDigit = this.GetLargestDigit(0, end);</font>

    <font size="2">      int num4 = (int)Math.Floor(Math.Log(Math.Max(-((double)decimal.MinValue), (double)decimal.MaxValue), (double)end));</font>

    <font size="2">      if ((largestDigit != 0) || (num4 == 1))</font>

    <font size="2">      {</font>

    <font size="2">          num5 = largestDigit;</font>

    <font size="2">      }</font>

    <font size="2">      else</font>

    <font size="2">      {</font>

    <font size="2">          num5 = this.GetLargestDigit(1, end);</font>

    <font size="2">      }</font>

    <font size="2">      for (int i = 0; i < num4; i++)</font>

    <font size="2">      {</font>

    <font size="2">          num5 = (num5 * end) + largestDigit; // Overflow exception.</font>

    <font size="2">      }</font>

    <font size="2">  }</font>

    <font size="2"> </font>

    <font size="2">  private int GetLargestDigit(int start, int end)</font>

    <font size="2">  {</font>

    <font size="2">      int num = -1;</font>

    <font size="2">      int width = -1;</font>

    <font size="2">      for (int i = start; i < end; i++)</font>

    <font size="2">      {</font>

    <font size="2">          char ch;</font>

    <font size="2">          if (i < 10)</font>

    <font size="2">          {</font>

    <font size="2">              ch = i.ToString(CultureInfo.InvariantCulture)[0];</font>

    <font size="2">          }</font>

    <font size="2">          else</font>

    <font size="2">          {</font>

    <font size="2">              ch = (char) (0x41 + (i - 10));</font>

    <font size="2">          }</font>

    <font size="2">          Size size = TextRenderer.MeasureText(ch.ToString(), this.Font);</font>

    <font size="2">          if (size.Width >= width)</font>

    <font size="2">          {</font>

    <font size="2">              width = size.Width;</font>

    <font size="2">              num = i;</font>

    <font size="2">          }</font>

    <font size="2">      }</font>

    <font size="2">      return num;</font>

    <font size="2">  }</font>

    <font size="2">}</font>

    <font size="2">
    </font>


  • I don't think it's a "strange" overflow, looks perfectly reasonable to me. I don't know .NET, so I may have the details wrong, but if I understand correctly, the maximal value for a base-10 decimal is 10^28-1, the minimal value is probably -(10^28-1) or -10^28. Converting to double gives ±1e28, abs, take the logarithm to base 10, get num4 = 28.

    Now we have num5 == largestDigit = 9 = 10^1-1. 


    for (int i = 0; i < num4; i++)

            {

                num5 = (num5 * end) + largestDigit;

            }  


    1. i = 0: num5 = 10*9+9 == 10^2-1
    2. i = 1: num5 = 10*(10^2-1)+9 == 10^3-1
    3. etc. until i = 26: num5 = 10*(10^27-1)+9 == 10^28-1
    4. i = 27: num5 = 10*(10^28-1)+9 == oops, overflow.
    Be careful with doubles.


  • Yeah, it has just a tiny overflow. But the exception occurres, if they just used a double for the num5 variables there would have been no problem. Ahwell, just to make it easy I devided the decimals min and max by 10. I don't think we will use much larger then that =P, and it works now. Also noticed in their calculations they forget that there could be a negative symbol in the front.

     

    Greetz


  • Garbage Person

    Could it perhaps be because that control is bloody fucking useless? 


Log in to reply