Comment On A Y'ed Gap

Schien Dong came across this function that calculates the spacing of numbers on the Y-axis of a charting component. It works, but in a very mysterious way .... [expand full text]
« PrevPage 1Next »

Re: A Y'ed Gap

2005-04-05 13:52 • by Brendan Kidwell
// 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...
}
}


Re: A Y'ed Gap

2005-04-05 13:58 • by crlf
I like the way it will round 1450 up to 2000 :)

Re: A Y'ed Gap

2005-04-05 14:09 • by diaphanein
32308 in reply to 32307

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.

Re: A Y'ed Gap

2005-04-05 14:10 • by anon
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);

}

Re: A Y'ed Gap

2005-04-05 14:16 • by dubwai
32310 in reply to 32308
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.

Re: A Y'ed Gap

2005-04-05 14:38 • by Javier G. Lozano
32311 in reply to 32309

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

Re: A Y'ed Gap

2005-04-05 14:38 • by anon
32312 in reply to 32309
Oops, this is better :)



private int CalculateGap(int min, int max, int interval)

{

    int gap = (max - min) / interval;

    return (int)Math.Round(gap, 0);

}

Re: A Y'ed Gap

2005-04-05 14:50 • by j m
Personally I'd be happier with...

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

And so on.

Re: A Y'ed Gap

2005-04-05 15:00 • by D. Philippe

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.

Re: A Y'ed Gap

2005-04-05 15:28 • by Sweets
32317 in reply to 32315
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 :)

Re: A Y'ed Gap

2005-04-05 16:02 • by RyGuy
32318 in reply to 32307

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.

Re: A Y'ed Gap

2005-04-05 16:04 • by RyGuy
32319 in reply to 32307

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.

My (more thorough) implementation.

2005-04-05 16:16 • by Charles Nadolski
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

            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

            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;

    }

}



Re: A Y'ed Gap

2005-04-05 16:38 • by crlf
32321 in reply to 32319
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.

Re: A Y'ed Gap

2005-04-05 16:47 • by Fred Flintstone
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);
}

Re: A Y'ed Gap

2005-04-05 18:20 • by KevMar
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

Re: A Y'ed Gap

2005-04-05 18:36 • by dubwai
32325 in reply to 32318
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.)

Re: A Y'ed Gap

2005-04-06 00:32 • by anon
32327 in reply to 32322
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);

}


Re: A Y'ed Gap

2005-04-06 08:15 • by Ion Todirel

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;
  }

Re: A Y'ed Gap

2005-04-06 18:57 • by 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.

Re: A Y'ed Gap

2005-04-06 22:48 • by Anonymous troll
32364 in reply to 32361
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. 

Re: A Y'ed Gap

2005-04-07 05:17 • by couch
32375 in reply to 32321
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).

Re: A Y'ed Gap

2005-04-07 07:27 • by Angelastic
32382 in reply to 32361

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()

Betreft: Re: A Y'ed Gap

2005-04-07 08:49 • by Katja Bergman
32384 in reply to 32375
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?

Re: Betreft: Re: A Y'ed Gap

2005-04-07 10:05 • by Charles Nadolski
32387 in reply to 32384
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.

Re: Betreft: Re: A Y'ed Gap

2005-04-07 10:13 • by Charles Nadolski
32388 in reply to 32387
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.

Re: A Y'ed Gap

2005-04-07 12:25 • by Kink
32400 in reply to 32361
In response to Schoool-Re-Lea's post, the code is written in Java, not C#. I guess that won't matter though.

Re: A Y'ed Gap

2005-04-07 13:46 • by Fred Flintstone
32403 in reply to 32327
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.

Re: A Y'ed Gap

2005-04-07 14:08 • by Fred Flintstone
32409 in reply to 32327
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.

Re: A Y'ed Gap

2005-04-08 10:42 • by dubwai
32444 in reply to 32382

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.

Re: A Y'ed Gap

2005-04-08 10:52 • by dubwai
32445 in reply to 32444

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);

Re: A Y'ed Gap

2005-04-09 15:03 • by Schol-R-LEA
32504 in reply to 32400
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.


Re: A Y'ed Gap

2005-04-11 06:42 • by fatgeekuk

So, that routine will round up 045 to 100!


 


thats rubbish!

Betreft: Re: A Y'ed Gap

2005-04-12 08:19 • by Katja Bergman
32602 in reply to 32522
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! [:)]

Re: My (more thorough) implementation.

2005-04-12 12:44 • by anonymous
32632 in reply to 32320
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.

Re: A Y'ed Gap

2009-05-18 04:18 • by wow gold (unregistered)
Out of runes of magic gold? Need it in urgent? Yes, I can understand you. As the most important currency, without rom gold, you ever can’t do anything. So you need to buy rom gold from those most professional and loyal game online shops with years’ experience and have a good reputation among players. Is there any difficult? No, when you need the rom gold, please feel free to contact us, we are promising to offer you the cheap runes of magic gold with fastest delivery. Moreover, we are online 24/7, you can contact us any time with any question about. So why are you still irresolute? Come here to grab your cheap runes of magic gold now. Crazy about running warhammer gold? Yup, it is so crucial indeed for us in Warhammer Online. Without it, we can even do nothing, without money to buy items, weapons and so on. So enough warhammer gold is substantial.
« PrevPage 1Next »

Add Comment