• Mr. Wumpus (unregistered)

    Once I almost did something similar. It was the first time I was stoned and my friends challenged me to write a program that draws a circle. The circle part was easy, but I couldn't for the life of me neither find nor remember which function in Delphi can be used to convert floats to integers. So I started to write a function that converts a float to string, finds the decimal point, cuts off the fraction and converts the string to integer. But, of course, converting a float to string proved to be an equally challenging task :)

    And then after 25 minutes of such creative process, a thought came to my head: what if the function is named something like FloatToDecimal :)

  • DeMoN (unregistered)

    Be honest folks ...

    Each of us dit (in ancient times) a implementation like that  [;)] ... *hrhrhr*

     

  • (cs) in reply to foxyshadis
    foxyshadis:
    Anonymous:
    Doesn't .Net do banker's rounding, by default? Wouldn't you have to re-implement round do get it to do standard arithmetic round?

    By default, but it has a flag to switch to standard rounding. I think it even includes nightmare ones like the swiss in an i18n package, but don't quote me.


    Holy crap, I thought it was only VB6 and VBScript that did bankers' rounding.  I just tested it in C# and it does in fact do it there too.  Looks like a company I used to work for may get screwed out of a few cents every now and then. ;)

    Where is the flag to switch to another rounding method?  I did some cursory searching and couldn't find it in any of the Math.Round overloads.
  • (cs)

    Among all the other minor and major WTFs in this code, it seems like a pretty subtle issue that he always rounds the same way. A good rounding function:

    • alternates direction between odd and even numbers, so 0.5 will round to 0, but 1.5 will round to 2, and
    • rounds towards 0, meaning that 0.6 will round up to 1, but -0.6 will down round to -1.

    This avoids introducing a bias into your data by distributing the rounding jitter evenly to both directions.

    But of course, with all the other ways in which this code his wrong, it’s no surprise that the mathematical subtleties of rounding correctly escaped this warm body.

  • (cs) in reply to Aristotle Pagaltzis

    Ah, I see the issue has been commented on in the meantime.

  • (cs) in reply to Andrei

    Anonymous:
    <!--StartFragment --> 
    Anonymous:
    I know your CS professors always say "RAWR GLOBAL VARIABLES BAAAAD", but don't automatically dismiss the usefullness of global variables in certain situations. This awful rounding function isn't one of those situations, but I'm just saying.


    I actually find great use in having a static global class for multithreaded applications.

    Basically, having a program ID, a vector of separate threads comes in handy - that's how I got my server to work. Also, when having a GUI app in java, which can be dozens of levels deep (Below is an example), having global variables can easily tie a controller. Separate the gui and the driver code.

    And an example of gui?
    The application is a panel, which has multiple faces, each face having different panels grouping the data logically, and each panel has a layout that logically separates the component, each of which has multiple buttons and drivers.

    Having a central driver makes all of that simply "globalData.client.lobby.tableList.tables[0].seat[0].user = new user"

    And yes, I actually have code like that.

    That looks pretty terrible.  Sorry.

  • (cs) in reply to Aristotle Pagaltzis
    Aristotle Pagaltzis:

    Among all the other minor and major WTFs in this code, it seems like a pretty subtle issue that he always rounds the same way. A good rounding function:

    • alternates direction between odd and even numbers, so 0.5 will round to 0, but 1.5 will round to 2, and
    • rounds towards 0, meaning that 0.6 will round up to 1, but -0.6 will down round to -1.

    This avoids introducing a bias into your data by distributing the rounding jitter evenly to both directions.

    But of course, with all the other ways in which this code his wrong, it’s no surprise that the mathematical subtleties of rounding correctly escaped this warm body.

    If you want that kind of rounding, sure but if you want 'standard' rounding, I don't see why you would want to not fufill the requirements.

  • Rubinho (unregistered) in reply to Rank Amateur
    Rank Amateur:

    Anonymous:
    That intFractions part is pretty funky. From what I can tell, it converts numbers like 12.34 to 12 + (34 / intFractions). Why anyone would ever want to store fractions in a floating point value like that I have no idea... and why the weird special treatment for intFractions=32?

    intFractions = 4

    round_to_digits = 2

    Round_price(3.719) -> 182.75

    WTF kind of price rounding is that?



    719 / 4 = 179.75
    179.75 + 3 = 182.75

    In this case 3.719 is equivalent to 3 719/4 (3 and 719 quarters).  See?  The intFractions number is the denominator of a fraction, the decimal part of the number which is passed into Round_price() is the numerator. 
  • (cs) in reply to Aristotle Pagaltzis
    Aristotle Pagaltzis:

    * alternates direction between odd and even numbers, so 0.5 will round to 0, but 1.5 will round to 2


    Maybe my numbering skills have left me, but wouldn't this kind of system create clusters and gaps in your data, and create bias rather than distributing evenly? Using that rule, no rounding would ever produce an odd number!

    Aristotle Pagaltzis:

    rounds towards 0, meaning that 0.6 will round up to 1, but -0.6 will down round to -1.


    Rounding -0.6 to -1 is:
    A) not "rounding to 0" but, in that example, rounding away from 0;
    B) totally correct, since -0.6 is closer to -1 than to 0. Closest whole number.
  • (cs) in reply to dhromed

    dhromed:

    Maybe my numbering skills have left me, but wouldn't this kind of system create clusters and gaps in your data, and create bias rather than distributing evenly? Using that rule, no rounding would ever produce an odd number!

    It's only fives that get rounded to the nearest even.

  • (cs) in reply to dhromed

    dhromed:
    Aristotle Pagaltzis:

    * alternates direction between odd and even numbers, so 0.5 will round to 0, but 1.5 will round to 2


    Maybe my numbering skills have left me, but wouldn't this kind of system create clusters and gaps in your data, and create bias rather than distributing evenly? Using that rule, no rounding would ever produce an odd number!

    Round-half-up produces an upward bias on the data.  For example:

    4 = 2.5 + 1.5

    round-half-up: 2 + 3 = 5.

    bankers rounding: 2 + 2 = 4

    The reason is that there are 9 roundable digits.  1...9  In round-half-up rounding, 5 round up and 4 round down.  This increases the probablility that at the end up the day, the sum of all the accounts will be greater than the total you started with.

    Banker's rounding removes this bias and makes it more likely that won't happen.

     

  • smelliot (unregistered)

    Wow! I'm glad he wrote it, I'm not sure I'm adept enough to do it myself...


    www.lamecode.com

  • (cs) in reply to David
    Anonymous:
    Anonymous:

    Notwithstanding the fact that ANY global variables are badbadbad, isn't "intFractions" an undocumented global variable?

    Just... wow.

    Really?  How do you do frequently used constants then?  Do you declare them in every method you need them in or do you pass them to every method that needs them as a parameter?  Neither is preferable to maintaining a list of constants at the top of a header file or the body of your program. 



    I declare constants as constants and variables as variables.

    The problem with global variables is that they can be changed anywhere.  Constants do not change.  That is why they are called constants.

    Sincerely,

    Gene Wirchenko

  • (cs) in reply to Russ
    Anonymous:
    The function would have to be called a lot. I just whipped up a couple of trivial C programs that call a function 300 million times, the only difference being one of them passes a couple of long ints and the other one uses globals. Compiled with no optimisation, the speed difference is about 0.3 seconds. Compiled with the -O3 flag, that comes down to about 3/1000ths, on average (over about 10 executions of each app). I'm hard pressed to think of any situation, even in realtime or embedded application spaces, where a speed penalty of 3/1000ths of a second over 300 million distinct function calls is going to make a difference.


    3/1000 s 300,000,000 times comes to 90,000 seconds.  That is just over a day.  Maybe your realtime apps do not have as strict of timing requirements as others' do?

    Sincerely,

    Gene Wirchenko

  • (cs) in reply to Maestro
    Anonymous:

    CLng from mircrosoft is rubbish... It rounds to the nearest even number when the decimal component equals 0.5.  The Round function mentioned in this WTF is not available in all VB development environments so writing your own needs to be done.  This function has been taken out of context to the environment in which it was developed.

    The rounding I use (when neccessary) is as follows (assuming two decimal places):

    newvalue = fix(oldvalue*100+0.5)/100

    Fix truncates towards 0 and Int (as used in this WTF) rounds towards a smaller value, which is not good for negative values.



    CLng() uses symmetric rounding, and there is a good reason for it.  It helps cut down the bias in rounding.

    Unfortunately, I could not find it documented HOW it rounded.  Page after page of Microsoft docs simply stated that rounding rounded.

    The rounding is applied in more than just CLng().  Try 17.5\2.5.  The answer is 9, because 17.5 is rounded up to 18 and 2.5 is rounded down to 2.

    Sincerely,

    Gene Wirchenko

  • (cs) in reply to Gene Wirchenko

    Gene Wirchenko:
    Anonymous:
    The function would have to be called a lot. I just whipped up a couple of trivial C programs that call a function 300 million times, the only difference being one of them passes a couple of long ints and the other one uses globals. Compiled with no optimisation, the speed difference is about 0.3 seconds. Compiled with the -O3 flag, that comes down to about 3/1000ths, on average (over about 10 executions of each app). I'm hard pressed to think of any situation, even in realtime or embedded application spaces, where a speed penalty of 3/1000ths of a second over 300 million distinct function calls is going to make a difference.


    3/1000 s 300,000,000 times comes to 90,000 seconds.  That is just over a day.  Maybe your realtime apps do not have as strict of timing requirements as others' do?

    Sincerely,

    Gene Wirchenko

    You misunderstand. 0.0003 seconds per 300,000,000 method calls or 0.00000000001 [1*10^-11] seconds per method call.

  • Jason Nussbaum (unregistered) in reply to silverpie
    silverpie:
    (Another example of using a float, at least in its string form, in that way is in baseball stats, where .1 and .2 represent one-third and two-thirds of an inning; similarly the cricket usage of overs.balls, with a base of 6.)


    I haven't followed baseball that seriously in quite a while (I think before the last time the Jays won the Series), but...since when are there 'thirds' of innings?
    Is this the new menage-a-trois baseball?
  • (cs) in reply to Russ
    Anonymous:

    The function would have to be called a lot. I just whipped up a couple of trivial C programs that call a function 300 million times, the only difference being one of them passes a couple of long ints and the other one uses globals. Compiled with no optimisation, the speed difference is about 0.3 seconds. Compiled with the -O3 flag, that comes down to about 3/1000ths, on average (over about 10 executions of each app). I'm hard pressed to think of any situation, even in realtime or embedded application spaces, where a speed penalty of 3/1000ths of a second over 300 million distinct function calls is going to make a difference.

    #include<time.h>
    #include<stdio.h>

    int g = 0;

    int parm(int a){   return a+1; }

    int global() { return g+1; }

    int main()
    {
      int a = 0;
      int i;
      time_t start, end;

      start = time(0);

      for(i=0; i<300000000; i++) {
         parm(a);
      }

      end = time(0);
      printf("time for parm: %i secs\n", end-start);

      start = time(0);

      for(i=0; i<300000000; i++) {
         global();
      }

      end = time(0);
      printf("time for global: %i secs\n", end-start);

      return 0;
    }


    Result:
    % ./a.out
    time for parm: 20 secs
    time for global: 9 secs


    Over twice as fast.  The point is, it's worth knowing about these things if you want a faster program.

    There are two rules for when to optimize:

    1. Don't do it.
    2. (For experts only) Don't do it yet."

  • bit (unregistered) in reply to Maurits
    Maurits:

    But if you have lots of them, and you never call the associated functions, that's just a waste of space.

    Also they lead to hard-to-track bugs involving functions calling each other, and using the same global variable.

    Ahhh... nothing like a GOSUB. :)

  • (cs) in reply to loneprogrammer

    loneprogrammer:

    Result:
    % ./a.out
    time for parm: 20 secs
    time for global: 9 secs


    Over twice as fast.  The point is, it's worth knowing about these things if you want a faster program.

    What if you did a fair test where you set g on each call?  Did you consider the compiler may have optimized away the global version of the method because g never changes?

    Other issues:  How many times did you run this?  Running benchmarks like this in tandem will often give skewed results.  What if your anti-virus scanner kicked in during the second loop?  Also I've seen so many cases where switching the order of the loops will completely change the results. 

    0.00000003 vs. 0.000000066...

    You find that to be significant?

    And ignoring that, your numbers here are meaningless because your method is trivial.  Most methods actually do something more than increment an int.  Moreover, most apps are going to do a lot more than increment an int 300 million times and ignore the results on each call.  If that's the kind of app you are writing, then maybe it matters.  Otherwise, it's not going to matter.

  • (cs) in reply to Jason Nussbaum

    Anonymous:
    I haven't followed baseball that seriously in quite a while (I think before the last time the Jays won the Series), but...since when are there 'thirds' of innings?
    Is this the new menage-a-trois baseball?

    Each out recorded yields one-third of an inning pitched for whoever is pitching. It allows ERA to be figured properly when a pitching change happens mid-inning.

  • Syarzhuk (unregistered) in reply to silverpie

    you are not guaranteed that the decimal separator will be a period, so this code breaks on a lot of international settings

    Well, if you are using the US way of quoting prices, you sure don't care about non-English users of the application.  

  • (cs) in reply to rob

    Anonymous:
    Doesn't .Net do banker's rounding, by default? Wouldn't you have to re-implement round do get it to do standard arithmetic round?

    The snippet in this post is VB6 which also does banker's rounding.  I've not been able to find any switches that change this behavior in .NET, so please post them if you know.  I've found myself implementing a custom round function a number of times, usually in situations where the numbers have to match something (spreadsheet, mainframe, etc.) that uses a different rounding method, usually arithmetic.  MS has a custom VB6 Round function that they released in a KB awhile back that handles a number of different rounding types - converts to .NET fairly easily.  Obviously, whateverTF this guy is trying to do here doesn't appear in the article...

    http://support.microsoft.com/?kbid=196652

  • Jason Nussbaum (unregistered) in reply to silverpie
    silverpie:

    Anonymous:
    I haven't followed baseball that seriously in quite a while (I think before the last time the Jays won the Series), but...since when are there 'thirds' of innings?
    Is this the new menage-a-trois baseball?

    Each out recorded yields one-third of an inning pitched for whoever is pitching. It allows ERA to be figured properly when a pitching change happens mid-inning.



    Shouldn't it be sixths, then?
  • (cs) in reply to dubwai
    dubwai:

    Gene Wirchenko:
    Anonymous:
    The function would have to be called a lot. I just whipped up a couple of trivial C programs that call a function 300 million times, the only difference being one of them passes a couple of long ints and the other one uses globals. Compiled with no optimisation, the speed difference is about 0.3 seconds. Compiled with the -O3 flag, that comes down to about 3/1000ths, on average (over about 10 executions of each app). I'm hard pressed to think of any situation, even in realtime or embedded application spaces, where a speed penalty of 3/1000ths of a second over 300 million distinct function calls is going to make a difference.


    3/1000 s 300,000,000 times comes to 90,000 seconds.  That is just over a day.  Maybe your realtime apps do not have as strict of timing requirements as others' do?

    You misunderstand. 0.0003 seconds per 300,000,000 method calls or 0.00000000001 [1*10^-11] seconds per method call.


    Oh.  I thought you meant per call.  BTW, that is 0.003 not 0.0003.

    Sincerely,

    Gene Wirchenko

  • Russ (unregistered) in reply to loneprogrammer
    loneprogrammer:
    <time.h><stdio.h>
    Result:
    % ./a.out
    time for parm: 20 secs
    time for global: 9 secs


    Over twice as fast.  The point is, it's worth knowing about these things if you want a faster program.

    </stdio.h></time.h>

    There are two rules for when to optimize:

    1. Don't do it.
    2. (For experts only) Don't do it yet."



    20 seconds? Dude, are you running that test on an abacus or what?! ;-) Here's my implementation of your test (split into two apps):
    <font color="#ffff00"> 1</font>  <font color="#ff40ff">#include <stdio.h><stdio.h><stdio.h><stdio.h></stdio.h></stdio.h></stdio.h></font><font color="#ff6060"><stdio.h></stdio.h></font>
    <font color="#ffff00"> 2</font>
    <font color="#ffff00"> 3</font>  <font color="#00ff00">unsigned</font> <font color="#00ff00">long</font> g_a;
    <font color="#ffff00"> 4</font> <font color="#ffff00"> 5</font> <font color="#00ff00">unsigned</font> <font color="#00ff00">long</font> test_func(<font color="#00ff00">void</font>) {
    <font color="#ffff00"> 6</font> <font color="#ffff00">return</font> g_a + <font color="#ff6060">1</font>;
    <font color="#ffff00"> 7</font> }
    <font color="#ffff00"> 8</font> <font color="#ffff00"> 9</font> <font color="#00ff00">int</font> main() {
    <font color="#ffff00">10</font> <font color="#00ff00">unsigned</font> <font color="#00ff00">long</font> i;
    <font color="#ffff00">11</font> g_a = <font color="#ff6060">0</font>;
    <font color="#ffff00">12</font> <font color="#ffff00">for</font> (i = <font color="#ff6060">0</font>; i < <font color="#ff6060">300000000</font>; i++) {
    <font color="#ffff00">13</font> g_a = test_func();
    <font color="#ffff00">14</font> <font color="#ffff00">if</font> (g_a == <font color="#ff6060">0</font>) {
    <font color="#ffff00">15</font> printf(<font color="#ff6060">"failure? that's unpossible!</font><font color="#ff40ff">\n</font><font color="#ff6060">"</font>);
    <font color="#ffff00">16</font> <font color="#ffff00">break</font>;
    <font color="#ffff00">17</font> }
    <font color="#ffff00">18</font> }
    <font color="#ffff00">19</font> }
    <font color="#ffff00">20</font>
    and
    <font color="#ffff00"> 1</font>  <font color="#ff40ff">#include <stdio.h><stdio.h><stdio.h><stdio.h></stdio.h></stdio.h></stdio.h></font><font color="#ff6060"><stdio.h></stdio.h></font>
    <font color="#ffff00"> 2</font>
    <font color="#ffff00"> 3</font>  <font color="#00ff00">unsigned</font> <font color="#00ff00">long</font> test_func(<font color="#00ff00">unsigned</font> <font color="#00ff00">long</font> param) {
    <font color="#ffff00"> 4</font> <font color="#ffff00">return</font> param + <font color="#ff6060">1</font>;
    <font color="#ffff00"> 5</font> }
    <font color="#ffff00"> 6</font> <font color="#ffff00"> 7</font> <font color="#00ff00">int</font> main() {
    <font color="#ffff00"> 8</font> <font color="#00ff00">unsigned</font> <font color="#00ff00">long</font> a = <font color="#ff6060">0</font>;
    <font color="#ffff00"> 9</font> <font color="#00ff00">unsigned</font> <font color="#00ff00">long</font> i;
    <font color="#ffff00">10</font> <font color="#ffff00">for</font> (i = <font color="#ff6060">0</font>; i < <font color="#ff6060">300000000</font>; i++) {
    <font color="#ffff00">11</font> a = test_func(i);
    <font color="#ffff00">12</font> <font color="#ffff00">if</font> (a == <font color="#ff6060">0</font>) {
    <font color="#ffff00">13</font> printf(<font color="#ff6060">"</font><font color="#ff6060">failure? that's unpossible!</font><font color="#ff40ff">\n</font><font color="#ff6060">"</font>);
    <font color="#ffff00">14</font> <font color="#ffff00">break</font>;
    <font color="#ffff00">15</font> }
    <font color="#ffff00">16</font> }
    <font color="#ffff00">17</font> }
    <font color="#ffff00">18</font>
    Note how I use the return value (even though the test always fails) just to make sure the compiler doesn't do anything tricksy with unused data. On an Athlon 2Ghz, the global version runs in 1.66 seconds and the parameterised version in 1.88, so about 0.2 seconds difference. That's unoptimised. With the -O3 flag, the respective times are 0.296 and 0.299 seconds, so still a difference of 3/1000ths over the 300,000,000 calls.

  • rob (unregistered) in reply to Eric

    If someone knows how to use pre-written functionality in .Net (c#) to do arithmetic rounding, by all means, tell us.

    I have looked for it and could not find it. MS has implemented it for other stuff like excel, but not .net AFAIK. It does seem odd that they wouldn't, though.

    I just ended up implementing my own round. Apparently, no one ever noticed it was doing bankers rounding by default. Several rounding bugs were in the bug tracker for a while...

  • (cs) in reply to Jason Nussbaum
    Anonymous:
    silverpie:

    Anonymous:
    I haven't followed baseball that seriously in quite a while (I think before the last time the Jays won the Series), but...since when are there 'thirds' of innings?
    Is this the new menage-a-trois baseball?

    Each out recorded yields one-third of an inning pitched for whoever is pitching. It allows ERA to be figured properly when a pitching change happens mid-inning.



    Shouldn't it be sixths, then?

    It would be, but an "inning pitched" is actually a half-inning. Kind of like how "batting average" isn't an average at all (it's a percentage expressed as a decimal).

  • (cs) in reply to Syarzhuk
    Anonymous:

    you are not guaranteed that the decimal separator will be a period, so this code breaks on a lot of international settings

    Well, if you are using the US way of quoting prices, you sure don't care about non-English users of the application.  

    If you were getting data in string form, it would be provided by a US service, so that would be correct. But you're getting (into the function) a float and converting it to a string, using your own string format. That's something a Canadian brokerage might well have to do with US investments, and in the Québec/Moncton/Sudbury office, the settings on a given machine might well be French.

    At any rate, it's just a side effect of the initial dumb decision to take data in a numeric format and convert it to a string for the purpose of doing a math operation on it. (That raises another issue: what if a floating-point bug causes a 102.17 to appear as 102.1699999999?)

  • (cs) in reply to silverpie
    silverpie:
    That raises another issue: what if a floating-point bug causes a 102.17 to appear as 102.1699999999?


    Then you might define a "Close Enough" operator, in addition to ==, <=, >= etc.


    Or so I've heard.
  • (cs) in reply to dhromed

    dhromed:
    silverpie:
    That raises another issue: what if a floating-point bug causes a 102.17 to appear as 102.1699999999?


    Then you might define a "Close Enough" operator, in addition to ==, <=, >= etc.


    Or so I've heard.

    Context, man! In this function, your 102.17 (which should, of course, become 102.53125) would spit out 53'125'101.96875!

  • (cs) in reply to silverpie

    At any rate, it's just a side effect of the initial dumb decision to take data in a numeric format and convert it to a string for the purpose of doing a math operation on it. (That raises another issue: what if a floating-point bug causes a 102.17 to appear as 102.1699999999?)

    I disagree. I think the original dumb decision is to try and shoehorn 2 distinctly different data types into a single representation, without thinking which representation is the most appropriate (or, indeed, thinking at all)

    A better approach overall might well be to use an abstracted, hopefully exact[1], representation of values internally, and to convert to and from this as needed. Of course that's a bit[2] slower.

    The locale arguments still stand, of course.

    Simon

    [1] See http://support.microsoft.com/kb/q279755/ for a dumbed down explanation of floats for VB programmers, note "if accuracy is important to your calculation, use the Decimal data type" [2] for varying values of "a bit"

  • (cs) in reply to silverpie
    silverpie:

    Anonymous:
    I haven't followed baseball that seriously in quite a while (I think before the last time the Jays won the Series), but...since when are there 'thirds' of innings?
    Is this the new menage-a-trois baseball?

    Each out recorded yields one-third of an inning pitched for whoever is pitching. It allows ERA to be figured properly when a pitching change happens mid-inning.

     
    I had to comment on this... mid-inning means within an inning, not in the middle of an inning, since an inning has no middle (but it has thirds)!
  • (cs) in reply to DWalker59
    DWalker59:
    silverpie:

    Anonymous:
    I haven't followed baseball that seriously in quite a while (I think before the last time the Jays won the Series), but...since when are there 'thirds' of innings?
    Is this the new menage-a-trois baseball?

    Each out recorded yields one-third of an inning pitched for whoever is pitching. It allows ERA to be figured properly when a pitching change happens mid-inning.

     
    I had to comment on this... mid-inning means within an inning, not in the middle of an inning, since an inning has no middle (but it has thirds)!

    Right, using "inning" to mean half-inning. When "inning" means full inning (and I must deny US responsibility for this, as cricket has even more impressive confusion on the matter--there, the phrase "2nd innings" is ambiguous!), the pitcher always changes at the middle of an inning, not because of a substitution, but because the other team is now at bat [H]

    Boy, has this become a funny digression.

  • Suomynona (unregistered)

    Lots and lots of WTFs in that code, but I particularily like the way he passes a floating-point number with binary mantissa into the function, performs decimal rounding on it, and returns the result as a Double (with binary mantissa again), which cannot even store most decimal fractions losslessly. Curious results like Round_price(Round_price(x)) != Round_price(x) are practically guaranteed.

  • Suomynona (unregistered) in reply to boohiss
    Anonymous:
    I know your CS professors always say "RAWR GLOBAL VARIABLES BAAAAD", but don't automatically dismiss the usefullness of global variables in certain situations. This awful rounding function isn't one of those situations, but I'm just saying.


    If it takes you a CS professor to figure out that global variables are pure evil, you:

    a) had a lot of luck and never got burned
    b) are rather resistant to learning

    Hate to say it, but global variables don't only make hard to maintain and even understand, they automagically turn any code that uses them - whether directly or indirectly - into a non-reentrant mess. Now some coders may instinctively reply "My code doesn't use threads.", to which I respond:

    a) Not yet.
    b) Non-reentrant code doesn't mess with multi-threading alone, it also messes with recursion.

    Some people on this board have suggested that there was a contradiction between "global variables are evil" and the hype surrounding the singleton pattern. But they are fundamentally wrong. Global variables encapsulate, or let's say merely contain, plain old data. Singletons - if used properly - encapsulate behaviours. That's a difference like night and day, and anyone who uses classes as more than just containers for public fields can confirm that.

    Of course, it is possible to abuse singleton in a way that's no better than the use of global variables, but it's for instance easy to make the methods of a well-designed singleton class thread-safe or reentrant, or to alter/exchange its implementation without affecting the rest of the application (this is an important aspect of maintainability). Try that with a global variable.

  • Suomynona (unregistered) in reply to loneprogrammer
    loneprogrammer:

    int g = 0;

    int parm(int a){   return a+1; }

    int global() { return g+1; }

    /*** snip ***/


    The loops don't do anything, so a clever compiler might opt to eliminate them entirely. Which is actually exactly what GCC 3.4.4 does at -O3, at least judging from the 0 seconds execution time in both cases I get on this machine.

    The time measurement is rather crude, too - it uses 1 second granularity and "real time", not "process (user) time". I had to change i to an unsigned int and let both loops count to 4000000000u (4 billions) to actually see a difference at -O0 on the Athlon XP 2800+ I use here.

    But even then, with optimizations disabled, it's only about 10% - 32 vs. 29 seconds. However, please understand that I can't grant you even that small victory :o). That's because if you do have a function where function call overhead really matters, then by all means you should inline it and gain more performance than you would by using silly and unmaintainable global variable hacks.

  • (cs) in reply to Andrei

    Having a central driver makes all of that simply "globalData.client.lobby.tableList.tables[0].seat[0].user = new user"

    And yes, I actually have code like that.

    Eep!  A single line with intimate knowledge of  6 other classes or structures. Low coupling is the goal, not high.

Leave a comment on “Reinventing The Round”

Log In or post as a guest

Replying to comment #:

« Return to Article