• gnasher729 (unregistered)

    What's worse is that it displays all the circles if you are 90.001% complete. That should happen only at 100%.

  • TS (unregistered)

    And again, like Monday's post, they are confusing "percentage" with "fraction".

  • (nodebb)

    And of course percentage contains a fraction rather than an actual percentage...

  • Foo AKA Fooo (unregistered) in reply to Steve_The_Cynic

    That's alright. Actual percentage should be for display only; using fractions internally is good. So just a slight misnomer.

    What bothers me more is that instead of hard-coding all the cases, this could be done algorithmically. Not exactly rocket since and it would make it easier to fix the rounding (cf. gnasher's comment), change the character (when the boss decides we need the green circle rather than the blue one), or the width of the bar.

  • Smithers (unregistered)

    otherwise some user is going to be at 0% and see a completed progress bar.

    No they aren't. If percentage is ever computed as negative, that's a WTF somewhere else in the code and otherwise if it's close-but-not-quite-equal to 0, it's going to hit the (percentage > 0.0 && percentage <= 0.1) case. There's a bit of a WTF in that all the > checks are redundant, but that's minor.

  • Michael P (unregistered) in reply to Smithers

    I am glad somebody else made the same observation. The Real WTF is people confidently asserting things based on flawed virtual execution of source code.

    (I once got a resume with a few self-descriptions right below the candidate's name. The first self-description was "human compiler". I was not curious enough about why they thought that was a selling point to do a phone screen.)

  • MaxiTB (unregistered)

    I'm confused, this code is perfectly fine.

    That's fine in some places, like when you know percentage was initialized to 0, but when it's being calculated, it could easily be very close to zero without actually being zero, thanks to a loss of precision.

    The second line covers everything above 0. There is nothing wrong with checking against zero, 0D is in fact a concrete binary representation of double, no rounding issues whatsoever.

    That's why the Equals and CompareTo functions should be used, otherwise some user is going to be at 0% and see a completed progress bar.

    You never ever use Equals in C# with keyword types; the operators are the primary implementation, Equals is just an overwrite in case the ValueType is boxed. This includes strings. A very common mistake Java developers make.

    So yeah, this code is actually best practice how to handle floating point numbers in .net; it uses correct operators, follows correct binary representations (.net is after all ISO standardized open source, so those representations are clearly defined compared to other non-standardized proprietary languages like Java) and the code will even be optimized by Roslyn.

    The only thing is the mix-up between logic and representation, but maybe this is part of a view, so then this code is pretty much perfectly following best practices.

  • Hanzito (unregistered)

    You can check the rest of the code for yourself. It's been made public: https://github.com/MinBZK/woo-besluit-broncode-digid-app

  • (nodebb)

    All the percentage > X comparisons are unnecesary unless you can have negative values. Also can be converted to a nice little pattern matching:

    return percentage switch { < 0 => "FILE_NOT_FOUND", 0 => "⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪", <= 0.1 => "🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪", <= 0.2 => "🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪", <= 0.3 => "🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪", <= 0.4 => "🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪", <= 0.5 => "🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪", <= 0.6 => "🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪", <= 0.7 => "🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪", <= 0.8 => "🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪", <= 0.9 => "🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪", _ => "🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵", };

    Spacing and negatives handling added for your convenience.

    Addendum 2023-01-18 08:05: EDIT: Sorry about the formatting.

  • DQ (unregistered)

    I always like it when one (wo)men's WTF is another (wo)men's fine code :)

  • (nodebb)

    This isn't a WTF at all. This code is fine. Very easy to read, understand and maintain. Is it more verbose and explicit than it needs to be? Sure, you could compute the number of circles to output, but then the code would be much harder to understand at first glance. The only minor improvement I would suggest is to remove the redundant > checks.

  • Foo AKA Fooo (unregistered) in reply to tom103

    Sure, it makes it much easier to change it inconsistently. Change a number here and there, and suddenly you have a bar that runs slower and faster at different points, or a bar that jumps from blue to green circles if you change the character and overlook some. Fixing the rounding (so 90.1% is not shown as 100%, cf. gnasher's comment) properly involves changing about 20 numbers rather than one. And let's not talk about widening the bar to say 20 characters wide. Yeah, so easy to maintain!

    Come on, if simple multiplication and rounding is "much harder to read", you shouldn't be calling yourself a programmer.

  • (nodebb)

    Use a switch expression with pattern matching... common, aren't C# programmers using C# 8.0 yet?

  • Tim (unregistered) in reply to gnasher729

    I don't see anything wrong with displaying a full row when it's 90.01% complete. that depends on the customer's requirement. If the display disappears when it gets to actual 100%, then it makes a lot of sense to display all the dots when it gets to > 90

  • (nodebb)

    This code is terrible, where are the other 90 lines to check each percentage point?

  • davethepirate (unregistered)

    If you only display all the circles at 100%, you would probably never actually see it with all the circles and thus have to modify the code to do some sort of sleep once it is done just so the user can see all the circles lit up.

  • (nodebb) in reply to Foo AKA Fooo

    Come on, if simple multiplication and rounding is "much harder to read", you shouldn't be calling yourself a programmer.

    I wouldn't have trouble understanding what the code does, but it would definitely take longer than in its current form. And I'm just giving an opinion, no need to insult me...

  • (nodebb) in reply to Steve_The_Cynic

    I tend to think of percentages as integers, not doubles. If you need precision then showing percentages is not the right way

    For progress meters I would just round to integers as first step then display a string like (in Python)

    def GetPercentageRounds(progress): percentage = int(progress) return "🔵" * (percentage // 10) # between 1 and 10 progress characters - unicode round blue circles for reasons

    print(string_val)

  • (nodebb)

    The article author doesn't seem to understand how floating point works on computers.

    When you or I do floating point operations on paper or in our heads, if we use numbers with no end like pi or 0.3333.... we won't use the whole number but will cut it off at some reasonable poin, with the understanding the result can only be trusted to a certain number of decimal points.

    PCs work exactly the same way. Floating point numbers are stored in a binary form of scientific notation (base * 2 ^ exp) with a limited number of bits for the base and exponent. The bits in exponent determine the range possible. The bits in base determine the precision possible. So if the number gets cut off inthe base bits you get rounding error.

    Guess what? 0 can be perfectly represented with no rounding error.

  • (nodebb) in reply to Mr. TA

    They're in the PaulaBean.

  • (nodebb)

    Hmm, as a more flexible loop: (This should be C#)

    private static string GetPercentageRounds(double percentage)
    {
       int i;
       const int bar_size=10;
       StringBuilder result= new StringBuilder(bar_size);
    
       if (percentage <= 0.0)
          result.append("-", bar_size);
       else
          for (i=0; i<bar_size; ++i)
          {
             if (i*1.0/(bar_size+1) <= percentage)  // force calculation to be in double, not int.
                result.append("🔵");
             else
                result.append("⚪");
          }
    
       return result.ToString();
    }
    </pre>
    

    Addendum 2023-01-19 00:29: Actually, the (bar_size+1) should be just bar_size. That came from earlier attempt to make the bar appear all blue when it reaches 100%.

  • Foo AKA Fooo (unregistered) in reply to tom103
    Comment held for moderation.
  • (nodebb) in reply to Foo AKA Fooo

    Regarding the == 0 controvery: I was originally also agreeing, that the article is taking issue with the wrong thing here, as == 0.0 comparisons are actually reliable in floating point. However, it is forgetting to ask "how is the zero calculated".

    If the form is something like

    fraction = 1.0 * (total - current +1) / total
    

    where all variables on the right-hand side are integers? The result will be exactly 0.0. All is fine. But if instead "total" and "current" are already floating point variables, things get ickier.

    Let's say you have a list of tasks with assigned weights. Then you might have something like

    total = sum(weights)
    current = sum(weights[0:index-1]) + fraction_of_current_step * weights[index]
    

    and I wouldn't trust the result to be exactly 0.0 any more.

    That said, I'm also taking issue with the

    slight misnomer

    of "percent" when really it is "fraction". Calling the parameter "percent" is misleading in a way that can easily lead to bugs.

  • RLB (unregistered) in reply to Foo AKA Fooo

    Sure, it makes it much easier to change it inconsistently. Change a number here and there

    This is code for my national ID system. It is absolutely fundamental to all digital interactions with my government and allied agencies. This is not your employer's Scrummy move-fast-and-break-things sales app. Nobody is going to "change a number here and there".

  • (nodebb) in reply to Nutster

    @Nutster I haven't done anything with C# so I was sufficiently intrigued to try out your snippet as my "Hello Paula" plunge into the .NET pool ("When the dot-net comes around, you must whip it...") ...

    I found that your code didn't quite work as intended; I had to move that bar_size divisor over to the other side and divide percentage by it to get it starting to behave correctly.

    I also tweaked your function thusly to get it to scale for bar_size other than 10 (I tested for bar_size of 5 to 30 using an online C# compiler):

        private static string GetPercentageRounds(int percentage)
        {
            const int bar_size = 20;
            double scalefactor = bar_size/100.0;
            StringBuilder result = new StringBuilder(bar_size);
    
            if (percentage < 0 || percentage > 100) {
                return("FILE NOT FOUND");
            } else {
                for (int i = 0; i < bar_size; ++i) {
                    if ((i*1.0) <= (percentage*scalefactor)) {
                        result.Append("🔵");
                    } else {
                        result.Append("⚪");
                    }
                }
            }
            return result.ToString();
        }
    
  • MaxiTB (unregistered)

    I don't know why there are so many people here actively trying to kill the planet by generating multiple reference objects in an iteration instead of simply returning one out of 10 string literals. Must be Java developers, they really hate the planet :-)

  • (nodebb) in reply to MaxiTB

    It keeps us warm in a brutal East Coast winter. We warm ourselves and cook our food in the heat generated by these computations. Instead of casting aspersions on our survival tactics, why don't you go funroll a loop?

    Addendum 2023-01-19 08:08: 😛

  • owlstead (unregistered) in reply to gnasher729

    Usually, when hitting 100% the progress bar disappears and we can start to do something else. Similarly, as a user, I would like to see if anything is happening. So only displaying nothing when at zero percent is maybe not mathematically correct, but for the user it probably is more informative than still displaying zero.

    I'm not sure which programmers voted that as informative, but you should do some rethinking around the actual use case.

  • owlstead (unregistered) in reply to davethepirate

    Not displaying the full 100% is fine. Think about the actual user. They hit full circles and then the process stalls. That's not very nice, you give them a hint that they are there and then they get stuck anyway (Windows used to do this while installing things, although it might be that they just didn't expect the next operation to take a long time).

    You could maybe consider showing all circles when hitting 100% (or 1.0) but in that case you'd update the GUI only for it to immediately disappear, if it appears at all. There is a reason why we nowadays often see a circle going around, progress bars are often deceptive.

  • Gearhead (unregistered)

    They missed an opportunity to make a spinning ball.

    static int state = 0;
    if (++state==4) state = 0;
    switch (state) {
      case 0:  return "◐";
      case 1: return "◓";
      case 2: return "◑";
      case 3: return "◒";
      default: return "◕";
    }
    
  • (nodebb) in reply to Gearhead
    int state = percent / 25.0;
    switch (state) {
      case 0:  return "   \n + \n   ";
      case 1: return "## \n + \n   ";
      case 2: return "###\n +#\n   ";
      case 3: return "###\n +#\n ##";
      default: return "###\n#+#\n###";
    }
    
  • Gearhead (unregistered)

    Ha. Well done!

  • Pieroxy (unregistered)
    Comment held for moderation.

Leave a comment on “Rounding Percentages”

Log In or post as a guest

Replying to comment #:

« Return to Article