• Brendan Kidwell (unregistered)
    // better?

    for(int i = 0; i < 10000; i++) {
    switch(i) {
    case 0: if(i == gap) result = 1; break;
    case 1: if(i == gap) result = 1; break;
    case 2: if(i == gap) result = 1; break;
    case 3: if(i == gap) result = 1; break;
    case 4: if(i == gap) result = 1; break;
    case 5: if(i == gap) result = 2; break;
    case 6: if(i == gap) result = 2; break;
    case 7: if(i == gap) result = 2; break;
    case 8: if(i == gap) result = 2; break;
    case 9: if(i == gap) result = 2; break;
    case 10: if(i == gap) result = 2; break;
    case 11: if(i == gap) result = 2; break;
    case 12: if(i == gap) result = 2; break;
    // and so on...
    }
    }

  • crlf (unregistered)

    I like the way it will round 1450 up to 2000 :)

  • diaphanein (unregistered) in reply to crlf

    Alot of people don't realize there's more than one kind of rounding.  This appears to be similar to round-to-nearest-even (RNE).  There are statistical reasons why you don't always want to round up if the least-significant digit is >= 5, and down otherwise.  I've had to implement similar schemes in hardware.  Of course there, I wasn't parsing a string, and I was able to reduce the rounding logic to a 3-stage bitwise logic operation.

  • anon (unregistered)

    private int CalculateGap(int min, int max, int interval)
    {
        int gap = (max - min) / interval;

        int digits = (int)Math.Log10(gap);
        double roundingFactor = Math.Pow(10, digits);
        return (int)(Math.Round(gap / roundingFactor) * roundingFactor);
    }

  • dubwai (cs) in reply to diaphanein
    Anonymous:

    Alot of people don't realize there's more than one kind of rounding.  This appears to be similar to round-to-nearest-even (RNE).  There are statistical reasons why you don't always want to round up if the least-significant digit is >= 5, and down otherwise.  I've had to implement similar schemes in hardware.  Of course there, I wasn't parsing a string, and I was able to reduce the rounding logic to a 3-stage bitwise logic operation.

    No, I don't think this is 'round half even'.  It's round 45-50ths up.

    And implemented in a completely asinine way.

  • Javier G. Lozano (unregistered) in reply to anon

    Nice solution!  Although string manipulation is more efficient...(just kidding)[:P]

  • anon (unregistered) in reply to anon

    Oops, this is better :)

    private int CalculateGap(int min, int max, int interval)
    {
        int gap = (max - min) / interval;
        return (int)Math.Round(gap, 0);
    }

  • j m (unregistered)

    Personally I'd be happier with...

    gap % 10 (gap / 10) % 10 (gap / 10 / 10) % 10 (gap / 10 / 10 / 10) % 10

    And so on.

  • D. Philippe (unregistered)

    I've seen a lot of string-number handling problems in inexperienced programmers, especially among the managed languages crowd.  I think part of the problem lies in the academics at colleges.  Scenarios like this one, which are the bread-and-butter of any normal programming job, are usually glossed over because they tend to be language-specific (e.g., math libraries, string handling classes, etc.).  Take any kind of data structures classes and they'll spend weeks and weeks going over the abstract stuff like sorting algorithms and big-O notation while spending relatively little time teaching the things that matter in the real world.

    Anyway, just an observation from someone who attended CS classes at 3 different universities.

  • Sweets (cs) in reply to D. Philippe

    True; But you're supposed to learn the general/abstract stuff at school.
    You're not going to learn about Kruskal and Prim at your first job.

    That being said,  A programmer in their Junior year should of already done enough projects to build up the experience to handle a problem like this correctly. 
    Unless they had the exchange student that sits in the front row do all their projects :)

  • RyGuy (cs) in reply to crlf

    Seeing how the author didn't check for numbers with 4 or more digits, I don't think he ever had to worry about that scenario.

  • RyGuy (cs) in reply to crlf

    My above comment was in response to crlf's comment.

    I thought the forum software was going to put the quote in for me.  Guess I was wrong.

  • Charles Nadolski (cs)

    Here's a solution to a similar "y-gap" problem that I wrote.  It's meant to nearly replicate the way excel auto-scales axes in a graph.  It attempts to find the "ideal" spacing of 5-10 divisions on axes, given a min and a max.  You may notice one possible WTF in that I used with CString.Format() to round.  That's because MFC's math library only has ceil(), floor(), and binary mantissa functions, no "true" rounding functions.

    Feel free to tear it apart and make fun of me at your leisure.

    Note that much of the class has been snipped.  The proverbial meat without potatoes.  Huh, apparently I never wrote comments.  Have fun!

    void CGraphScale::SetMinMax(double dMin, double dMax)
    {
        m_dActualMin = dMin;
        m_dActualMax = dMax;
        int i, nMaxIter;
        CString strTemp;
        double dDiff = dMax - dMin;
        if(dDiff>1)
        {
            strTemp.Format("%.0f",ceil(dDiff));
            nMaxIter = strTemp.GetLength();
            strTemp = "1";
            for(i=0; i<nmaxiter ;="" i="">
                strTemp.AppendChar('0');
            m_dSpacing = atol(strTemp);
        }
        else if(dDiff>1e-16)
        {
            m_dSpacing = 1;
        }
        else
        {
            AfxMessageBox("Error: dMax must be greater than dMin.");
            ASSERT(true);
            return;
        }

        double dLabel, dMinLabel;
        for(m_nDivisions = 0; m_nDivisions<5;)
        {
            strTemp.Format("%.1e", m_dSpacing);
            switch(strTemp.GetAt(0))
            {
            case '1':
            case '5':
                m_dSpacing/=2;
                break;
            case '2':
                m_dSpacing/=2.5;
                break;
            }

            if(dMin>=0)
            {
                for(dLabel = 0; dLabel < dMin; dLabel+=m_dSpacing);
                if(dLabel!=dMin)
                    dMinLabel = dLabel-m_dSpacing;
                else
                    dMinLabel = dLabel;
            }
            else
            {
                for(dLabel = 0; dLabel > dMin; dLabel-=m_dSpacing);
                dMinLabel = dLabel;
            }

            m_nDivisions = dDiff / m_dSpacing;
            for(dLabel = dMinLabel + m_nDivisions * m_dSpacing;
                dLabel<dmax ;="">
                dLabel+=m_dSpacing, m_nDivisions++);
        }

        m_aTickMarks.SetSize(m_nDivisions+1);
        for(dLabel=dMinLabel, i=0; i<=m_nDivisions; dLabel+=m_dSpacing, i++)
        {
            if(abs(dLabel)<1e-16) dLabel = 0;
            m_aTickMarks[i]=dLabel;
        }
    }

    </dmax></nmaxiter>

  • crlf (unregistered) in reply to RyGuy
    RyGuy:

    My above comment was in response to crlf's comment.

    I thought the forum software was going to put the quote in for me.  Guess I was wrong.



    Still, it will round 145 up to 200 nevertheless.
  • Fred Flintstone (unregistered)

    I originally tried to use a simple formula with log, but it wasn't always accurate due to rounding. The following Java code runs about 10 times faster than the original (when translated into Java):

        public static int calculateGap2(int min, int max, int interval)
    {
    int gap = (max - min) / interval;
    if (gap < 10) return gap;
    if (gap < 100) return 10*((gap+5)/10);
    int mul = 100;
    while (gap >= 1000)
    {
    gap = gap / 10;
    mul = mul * 10;
    }
    return mul * ((gap+55)/100);
    }
  • KevMar (unregistered)
    Just take the first digit and a random number between 0 and 1.  If the random number is greater than .45, add one.
    Problem solved
  • dubwai (cs) in reply to RyGuy
    RyGuy:

    Seeing how the author didn't check for numbers with 4 or more digits, I don't think he ever had to worry about that scenario.

    If I'm not mistaken, it will work for 'any' length number.  It's only checking the first three digits because that's how rounding works (usually you only look at the first two but anyway.)

  • anon (unregistered) in reply to Fred Flintstone

    Fred Flintstone, what was the rounding problem?  Would the following not work?

    private int calculateGap(int min, int max, int interval)
    {
        int gap = (max - min) / interval;

        int digits = (int)(Math.log(gap) / Math.log(10));
        double roundingFactor = Math.pow(10, digits);
        return (int)(Math.round(gap / roundingFactor) * roundingFactor);
    }

  • Ion Todirel (unregistered)

    PointF[] ChartData(string funct, int xmin, int xmax, int npts) {
      PointF[] pts = new PointF[npts];
       
      float step = (xmax-xmin)/(float)(npts-1);

      for(int j = 0; j < npts; j++) {
        pts[j].X = (float)(xmin + step*j);
        pts[j].Y = (float)(Expression.expression.Evalute(funct, new string[]{"x"}, new double[]{pts[j].X}));
      }

       return pts;
      }

  • Schol-R-LEA (cs)

    Actually, it's even more absurd than most of the respondents realize. Why? Because a close look at the code shows that it is in C#, and (as one of the anonymous poster showed) the standard C#.Net Math.Round() method takes a precision argument.

  • Anonymous troll (unregistered) in reply to Schol-R-LEA

    I see three optimizations here:

    1.  The function takes min, max, and interval arguments.  This saves on one of those expensive floating point subtraction operations.

    2.  Hand coded loop unrolling in assigning the digit variables.  Sometimes "for" loops are just too slow.  

    3.  The variable "result" is declared near the top of the function and initialized to zero.  Then, at the bottom, it is assigned a value, and immediately returned.  Because if he had returned the expression "firstDigit * (int)Math.Pow(10, length - 1)" directly, it would take up a lot more space on the stack. 

    You're all just jealous because this guy is a l33t h4X0r and you're not. 

  • couch (unregistered) in reply to crlf
    Anonymous:

    Still, it will round 145 up to 200 nevertheless.


    You sure?

    It doesnt reevaluate secondnumber, so secondnumber will still be 4 so it will not be >=5.

    Rounds 145 to 155 (which is odd in itself mind).
  • Angelastic (unregistered) in reply to Schol-R-LEA

    Schol-R-LEA:
    Actually, it's even more absurd than most of the respondents realize. Why? Because a close look at the code shows that it is in C#, and (as one of the anonymous poster showed) the standard C#.Net Math.Round() method takes a precision argument.

     

    That would not do the same thing as the WTF code, so without the spec we have no way of knowing whether it would be a better or worse solution. If the spec says that numbers between 145 and 150 should be rounded up to 200, then Math.Round() would not be adequate. Stick to removing the WTF-ness of the actual code rather than solving a different, slightly simpler problem.

    I'm not saying that the WTF code can't be simplified, but that doesn't mean we should jump to using the simplest thing we can think of whether or not it actually does what it's supposed to. We don't know exactly how the code was supposed to behave, and we could guess that the x45 thing was a bug, but it might actually be a requirement, and the very reason the programmer chose to implement it this way instead of using Math.Round()

  • Katja Bergman (cs) in reply to couch
    Anonymous:
    Anonymous:

    Still, it will round 145 up to 200 nevertheless.


    You sure?

    It doesnt reevaluate secondnumber, so secondnumber will still be 4 so it will not be >=5.

    Rounds 145 to 155 (which is odd in itself mind).


    Now, you're wrong too. In the end, 145 will be round up to 200 because he first evaluates the third digit, then the second one.

    Start with 145.
    firstDigit -> 1
    secondDigit -> 4
    thirdDigit -> 5
    Since thirdDigit >= 5, secondDigit -> SecondDigit + 1 -> secondDigit -> 5
    Then secondDigit >= 5, firstDigit -> firstDigit + 1 -> firstDigit -> 5
    Finally, result -> firstDigit x 100 -> 200.

    However, keep in mind that 14567890 will also be rounded to 20000000 because it starts with 145.

    One fun thing though... What would happen if Max < Min? [:D] Or worse, when Max equals Min? [:D] Okay, we all know whe should not return 0 as Interval but what if we give a negative Interval?
  • Charles Nadolski (cs) in reply to Katja Bergman

    You might scream WTF at this solution but it's guaranteed to work in Microsoft Visual Studio C++:

    Laziest solution to rounding a number:

    double dSomeNumber = 1.2345;
    CString strTemp;
    //Rounding to two significant digits
    strTemp.Format("%.1e", dSomeNumber);
    dSomeNumber = atof(strTemp);

    Change the number between . and e to change the digits of precision.
    Bingo.

    My lengthier post above this one uses a similar technique that creates an object (containing an array of numbers) that mimics Excel's way of auto-formatting the labels on an axis.  I will give the full .h and .cpp if somebody replies.

  • Charles Nadolski (cs) in reply to Charles Nadolski

    Morover, the reason why the long code I posted is longer than everybody else's is because it takes into consideration many error situations:

    1. negative numbers
    2. fractions
    3. the minimum and maximum labels are guaranteed to encompass the true minimum and maximum
    4. the labels generated are guaranteed to be legible (only one or two digits of precision)
    5. maximum is less than minimum.
    6. It looks almost exactly like excel did it (a boon for intolerant customers who get a hardon for M$ products).

    One way to improve my code would be to add an extra argument for a target number of intervals.  Right now it assumes that mere mortals can only handle between 5 and 10 intervals, and chooses the best number of intervals to fit the data.
  • Kink (cs) in reply to Schol-R-LEA

    In response to Schoool-Re-Lea's post, the code is written in Java, not C#. I guess that won't matter though.

  • Fred Flintstone (unregistered) in reply to anon
    Anonymous:
    Fred Flintstone, what was the rounding problem?  Would the following not work?

    private int calculateGap(int min, int max, int interval)
    {
        int gap = (max - min) / interval;

        int digits = (int)(Math.log(gap) / Math.log(10));
        double roundingFactor = Math.pow(10, digits);
        return (int)(Math.round(gap / roundingFactor) * roundingFactor);
    }



    The original code would round 145 up to 200 (1450->2000, 14500->20000, etc.).  This solution does not.
  • Fred Flintstone (unregistered) in reply to anon
    Anonymous:
    Fred Flintstone, what was the rounding problem?


    In theory, this should work:
        public static int calculateGap(int min, int max, int interval)
    {
    int gap = (max - min) / interval;

    double l10 = log10(gap); // log10 is defined as log(x)/log(10)
    int digits = (int) l10;
    double g = Math.pow(10, l10 - digits)+0.55;
    return (int) g * (int) Math.pow(10.0, digits);
    }
    The problem is that when it should get a value of 2, it gets 1.999999999. You can fudge it by adding 0.5500000001, but that's kinda ugly. Besides, the pow and log functions are quite a bit slower. Ignoring the fact that your original algorithm didn't handle values like 145 correctly, it is still about 10 times slower than the iterative solution I posted. The above is about 1.5 times slower than your solution, but does handle 145 correctly.
  • dubwai (cs) in reply to Angelastic

    Anonymous:
    We don't know exactly how the code was supposed to behave, and we could guess that the x45 thing was a bug, but it might actually be a requirement, and the very reason the programmer chose to implement it this way instead of using Math.Round()

    At my last job our client was actually requesting this mode of rounding.  We were still trying to talk them out of it when I left.

    In any event, it's still not neccesary to convert to Stings to acomplish this.  You can do it in a number of ways, including (but not limited to) doing two half up rounds.  If I were to rewrite this (and wasn't concernd with floating point errors) and was pretty sure it was a one time need, I'd probably do something like:

    double temp = rawGap;

    while (temp >= 100) temp = temp / 10;

    temp = Math.round(temp);

    temp = temp / 10;

    temp = Math.round(temp);

    Which isn't neccesarily more performant than the original, but it's a lot easier to understand IMO.

  • dubwai (cs) in reply to dubwai

    Here's the ful solution of the above.  I think it works, but only if raw gap >= 1 , of course.  The original solution would also fail for numbers smaller than 1, I believe.

    double temp = rawGap;
    int size = 0;

    while (temp >= 100) {
        i++;
        temp = temp / 10;
    }

    temp = Math.round(temp);

    if (temp >= 10) {
        i++;
        temp = temp / 10;
    }

    temp = Math.round(temp);

    return temp * Math.pow(10, i);

  • Schol-R-LEA (cs) in reply to Kink
    Kink:
    In response to Schoool-Re-Lea's post, the code is written in Java, not C#. I guess that won't matter though.


    If it is, then the coder must have been studying C# at the time he/she wrote it and got confused, because there are at least three things I can see here that would be wrong if it were Java code:

    • The strings are declared "string", whereas the string class in Java begins with an uppercase 'S'.
    • Conversely, the String method "Substring()" begins witha lowercase 's' in Java, and the Math class method  "Pow()" with a lowercase 'p'.
    • The String->Integer method is "int.Parse()", whereas in Java the equivalent is "Integer.parseInt()". This is particularly notable since in Java "int" is a prinitive class and does not have any associated methods.
    Note that I am not a C# programmer; I noticed this not because it was correct C# (I don't know if it is or not, though it seems to be from what I've been able to look up), but because I knew it was not valid Java.
  • fatgeekuk (unregistered)

    So, that routine will round up 045 to 100!

     

    thats rubbish!

  • Katja Bergman (cs) in reply to fatgeekuk
    Anonymous:
    So, that routine will round up 045 to 100!

     

    thats rubbish!


    Rubbish? Yep, but it is the functionality that the original programmer wanted to have, apparantly, with his WTF code. Yes, it seems crazy and perhaps it is even crazy but it wouldn't suprise me that this has even been part of the original design! [:)]

  • anonymous (unregistered) in reply to Charles Nadolski

    Try floor( x + 0.5 ), if there's no round() function.
    Forgive me, but I didn't look at the code to see if you needed nearest-even behavior, or some other atypical rounding.

  • wow gold (unregistered)
    Comment held for moderation.
  • The Distant Future (unregistered)

    public String getPaula(int toRound) { String paula = "brillant";

    return paula;
    

    }

  • steve (unregistered)

    Assuming that gap cannot be negative, this function is equivalent:

    private int CalculateGap(int min, int max, int interval)
    {
    	int gap = (int)((max - min) / interval);
    	int result = 1;
    	
    	while (gap > 10)
    	{
    		if (gap < 1000 && gap % 10 > 4) gap += 5;
    		gap /= 10;
    		result *= 10;
    	}
    	
    	return gap * result;
    }

    I am not actually sure what the original code would do for a negative gap. I assume that it would crash on the hyphen character here:

    firstDigit = int.Parse(gapString.Substring(0, 1));

Leave a comment on “A Y'ed Gap”

Log In or post as a guest

Replying to comment #:

« Return to Article