QB64.org Forum

Active Forums => QB64 Discussion => Topic started by: Pete on September 10, 2021, 01:51:30 am

Title: I don't often work with numbers, but when I do....
Post by: Pete on September 10, 2021, 01:51:30 am
So I ran across this old rounding routine I made several years ago, which rounds up decimals like .49 to .5, etc. it converts the numeric variable to a string, rounds it, and then returns it as a rounded numeric variable. All well and good, until I came across this input...

.49999999

That should have came out as .5, but rounded up to 1 in my routine. Now my routine does takes anything .5 and up and makes it one. So what I discovered is QB64 does this when it encounters this many digits in an input statement...

input num. User input .49999999

So while on might expect the variable num to be .49999999, QB64 rounds it to .5. Now it will remain .49999999 if the precision is declared as: input num#. What I think is strange is the num is the same as num! but if you write...

PRINT .49999999

Well, that comes out as .49999999. It doesn't get rounded up. I would have thought PRINT .49999999 was the same as PRINT .49999999! but it isn't. PRINT .49999999 returns .5. So it seems PRINT .49999999 acts more like PRINT .4999999# or some other higher precision.

Oh well, I only use string math now, so that old routine just got a trip to the recycle bin.

Pete

Title: Re: I don't often work with numbers, but when I do....
Post by: jack on September 10, 2021, 02:26:15 am
I thought that QB64 had a using string function like print using but it does not seem so, neither does it have the VB format function
but you can use snprintf
Code: QB64: [Select]
  1.     SUB snprintf (Dest AS STRING, BYVAL l AS LONG, frmt AS STRING, BYVAL x AS DOUBLE)
  2.  
  3.  
  4. x = .49999999
  5. s = SPC(64)
  6. frmt = "%12.1f"
  7. CALL snprintf(s, 64, frmt, x)
  8. s = LTRIM$(s)
  9.  
Title: Re: I don't often work with numbers, but when I do....
Post by: RhoSigma on September 10, 2021, 02:30:51 am
Because 0.5 is one of this floating point numbers, which nerver can be represented in binary form exactly. It's because of the way floating points will be converted into the binary system and represented in the SINGLE/DOUBLE/EXTENDED IEEE formats.

0.5 will always result into an infinite period in the mantissa part of the IEEE formats. Some more advanced printing routines will consider such infinite periods and display it as the human would expect it, but the simple PRINT certainly not.

Will look if I still find my old example, but may be @George McGinn may give a better explaination as a member of the IEEE.
Title: Re: I don't often work with numbers, but when I do....
Post by: RhoSigma on September 10, 2021, 02:39:32 am
Oh damn, forget my last post, it was 0.1 not 0.5 what came to my memory about this matter. It should be possible to exactly represent 0.5, but not 0.1
Title: Re: I don't often work with numbers, but when I do....
Post by: jack on September 10, 2021, 03:03:25 am
not being able to edit your post is a discouragement from posting anything especially code, I was going to change
Code: QB64: [Select]
  1. CALL snprintf(s, 64, frmt, x)
to
Code: QB64: [Select]
  1. CALL snprintf(s, Len(s), frmt, x)
  2.  
Title: Re: I don't often work with numbers, but when I do....
Post by: RhoSigma on September 10, 2021, 04:03:26 am
not being able to edit your post is a discouragement from posting anything especially code, ....

Indeed, it's the worst change the admins did lately, especially for the Progams board. See the post from my signature. Over the years I've improved the wording and appearance of that post to be my designated place to publish my projects to the community, so that I just needed to update the program archives.

Now I've to create a new post and upload a 6MB archive every time I've an update:
1. Waste of database space
2. Old and outdated code/archives remain - the admins removed the link to the historical QB64 versions from the homepage to encourage people to only use the newest QB64 version available for download, but on the other hand they block people here to encourage the same for their projects.

Conclusion:
No more updates here from my side, will just change my signature link to redirect to my own hosting page, when the next updates are ready.

This forum unfortunately does lose its focus more and more, lately even some of my posts disappeared, cause obviously big parts of the BUGS board and of the current DEVELOPMENT topic were deleted without any notice.
Title: Re: I don't often work with numbers, but when I do....
Post by: FellippeHeitor on September 10, 2021, 11:12:45 am
I understand the frustration, but unfortunately being able to indefinitely edit one's own posts was being abused. If there was a way to do it selectively by board, I would have set it that way. Our forum software doesn't support the feature, though.

Regarding the BUGS board: posts with SOLVED bugs have been indeed removed. Bugs pending of fixes are still there. Do not consider that board to be permanent anyway, or bugs would too.

Regarding the DEVELOPMENT topic, I am the last one to ever want anything related to it to disappear, since I am the one implementing the new feature. The deletion there was indeed an accident.
Title: Re: I don't often work with numbers, but when I do....
Post by: SMcNeill on September 10, 2021, 12:45:55 pm
How does one abuse editing their own posts?

And even if one person could do such a thing, why not just ban the offenders, rather than eliminate the functionality from all the rest of the users?
Title: Re: I don't often work with numbers, but when I do....
Post by: George McGinn on September 10, 2021, 12:51:38 pm
I think removing solved bugs is a big mistake.

Suppose I was to search for an issue that I am having. I will never know if a solution existed because it was removed. Maybe the solution was a change to the way I use a statement, but that will never be found, and I will wind up asking the same question again, and people will wonder where I have been.

Or a bug may have been solved by a new development release, and since not everyone is on Discord, they will never see the conversations regarding the bug, if it is ever mentioned there. Again, I may be running an earlier release of QB64, and if I never find the issue I am having using a search on the forum, I will again bring it up.

I would discourage removing solved bugs, or maybe create a read-only forum topic labelled "Solved Bugs" so people can search it for possible solutions to their problems.


I understand the frustration, but unfortunately being able to indefinitely edit one's own posts was being abused. If there was a way to do it selectively by board, I would have set it that way. Our forum software doesn't support the feature, though.

Regarding the BUGS board: posts with SOLVED bugs have been indeed removed. Bugs pending of fixes are still there. Do not consider that board to be permanent anyway, or bugs would too.

Regarding the DEVELOPMENT topic, I am the last one to ever want anything related to it to disappear, since I am the one implementing the new feature. The deletion there was indeed an accident.
Title: Re: I don't often work with numbers, but when I do....
Post by: jack on September 10, 2021, 01:10:06 pm
How does one abuse editing their own posts?

And even if one person could do such a thing, why not just ban the offenders, rather than eliminate the functionality from all the rest of the users?
I am with you, and odin, you won't hurt my feelings if you ban me
Title: Re: I don't often work with numbers, but when I do....
Post by: FellippeHeitor on September 10, 2021, 01:34:38 pm
Maybe the solution was a change to the way I use a statement

Posts reporting real bugs go into that board. The scenarios you point out remain in Discussion.

How does one abuse editing their own posts?

Easily.

why not just ban the offenders, rather than eliminate the functionality from all the rest of the users?

Instead of kicking every stray cat entering the cat door, we closed the cat door. I'm sure you'll see how easier a solution that has been.

To everyone in need to update their existing post/code: use the "mark best answer/most recent code" functionality, as already stated before.

Let us please bring this thread back to its original topic.
Title: Re: I don't often work with numbers, but when I do....
Post by: George McGinn on September 10, 2021, 02:10:03 pm
@FellippeHeitor, That I understand. But what about my second scenario, where a bug was fixed, and say I am a regular or even a new user of QB64, or I'm an occasional user, and you delete a solved bug where it was not corrected in the stable release, yet was fixed in the development build (I'm not talking about major updates, like DEBUG, which you do post in the discussions).  How do I, with limited knowledge of how the community works (or maybe I am not that interested on going deeper into how our community works - I know we have users that are like that, and no, no names).

If you delete these solved bugs, I will never know if it was fixed (I'm betting many do not check GitHub either for commits to bugs). I'm a simple person who just wants to learn programming or be a user of QB64. How then, do I go about finding whether a bug from the stable release has a development fix, if you delete the solved bugs from the forum?

Yes, we can go around in circles debating this, but I do feel that for normal users, there needs to be a place where they can compare what they are experiencing with the location of possible fixes. When you move a discussion from the forum into bugs, then delete it, you remove the entire discussion thread from the forum.

There has to be a better way to preserve this information, or maybe in the BUGS forum section, add verbiage of where a person can find solutions to issues from the stable release.
 
I just think that the little bit of effort to preserve this information, or direct users to find it, will make the experience of using QB64 more enjoyable.

I know from my career in systems design and development, we always coded and documented for a person with a 3rd grade level. I guess back in the stone-age of computers (pre personal computers), not many were as literate as today's users are. I just think that providing for the simplest of users is more advantageous.

Posts reporting real bugs go into that board. The scenarios you point out remain in Discussion.
Title: Re: I don't often work with numbers, but when I do....
Post by: FellippeHeitor on September 10, 2021, 02:16:21 pm
Quote
I will never know if it was fixed

Use the latest version. If bug is still present, not been fixed. If it hasn't been fixed, but ever been reported, it'll still be in the BUGS board.

Best place to report bugs is the GitHub repository, for anyone interested.

Let us not make this thread get locked for derailing from the original topic, everyone.
Title: Re: I don't often work with numbers, but when I do....
Post by: Cobalt on September 10, 2021, 08:26:01 pm
How does one abuse editing their own posts?
And even if one person could do such a thing, why not just ban the offenders, rather than eliminate the functionality from all the rest of the users?

Where people have a tissy fit and go deleting all their posts for some bizarre reason. They leave a lot of `.` posts


Oh well, I only use string math now, so that old routine just got a trip to the recycle bin.
Pete

Why?

I always just did this:
Code: QB64: [Select]
  1. a = .4999999
  2. PRINT INT(a * 10 + .49) / 10
  3.  

Rounds it up to .5

BTY, You and numbers scare me.....
Title: Re: I don't often work with numbers, but when I do....
Post by: bplus on September 10, 2021, 09:27:29 pm
Didn't we go over rounding to some decimal place a number of times, 2.99999 I think.
Title: Re: I don't often work with numbers, but when I do....
Post by: SMcNeill on September 10, 2021, 10:07:32 pm
Didn't we go over rounding to some decimal place a number of times, 2.99999 I think.

We have, but nobody real cares about it. 

People think in base-10 and they’re just not interested to learn why their pc thinks in base-2.  It’s simply easier to bitch about the issue being a glitch and report it as a bug, than it is to understand that it’s expected behavior.

Folks could resolve a lot of these issues by simply just adding a _DEFINE A-Z AS _FLOAT at the top of their code and/or limiting input.

If an input of .49999999 reaches the limits of your single precision variable and rounds to .5, write your code to limit precision to one less decimal point.

.999999999 might be the same internal value as 1, but
.999999990 probably isn’t, if you’re limiting your user input properly.

People just need to learn: Floating Point Precision isn’t PERFECT PRECISION. 
Title: Re: I don't often work with numbers, but when I do....
Post by: Pete on September 11, 2021, 01:57:40 am
In regards to limiting input, that is an issue with the INPUT statement, because you can't manipulate it. So back we go to using the LINE INPUT, where the number is input as a string, and therefore is not manipulated by base 2 rounding. Without any declaring of the variable type, INPUT assigns the variable as SINGLE. BTW - I find it interesting that PRINT without a variable type declaration does not follow this same convention. Instead, PRINT assigns the variable as DOUBLE, go figure. I would think INPUT and PRINT should be on the same page here, but they clearly are not. You would need to define all INPUT variables as DOUBLE to be equivalent to PRINT. Meaning INPUT a# would allow .499999999999999 to remain that number after INPUT, as well as PRINT .499999999999999 would display the same. Add one more "9" to the end of either, and they both reach the max of their DOUBLE precision limits, and would both be rounded to .5.

So where should we go with this. Well, since you guys seem to appreciate graphics more than I, my recommendation would be to modify the INPUT statement so when the user is about to type that last digit, the Lost in Space robot appears in the IDE, waves its  arms frantically, and shouts, "Danger! Danger!" 

Pete
Title: Re: I don't often work with numbers, but when I do....
Post by: bplus on September 11, 2021, 12:32:44 pm
Oh sorry to get to Pete's point so slowly, it's about the inconsistency between how Print handles a number literal and how Input handles the same? maybe?

@Pete If so, don't let it hobgoblin you, use INPUT$  :-))

But if you do use INPUT ""; var##

Here's a nice rounding of a _Float from Steve (I may have modified it, it says simplified):
Code: QB64: [Select]
  1. For i = -3 To 12
  2.     Print i, Round##(12345.67899999, i);
  3.     Locate , 30: Print _Round(12345.67899999)
  4.  
  5. Function Round## (num As Double, digits As Long) ' Steve's simplified
  6.     Round## = Int(num * 10 ^ digits + .5) / 10 ^ digits
  7.  



Title: Re: I don't often work with numbers, but when I do....
Post by: Pete on September 13, 2021, 11:55:08 am
Applying that method, I see the following problem...

Code: QB64: [Select]
  1. i = 3
  2. a = .01
  3. FOR j = 1 TO 200
  4.     b = b + a
  5.     PRINT j, Round##(b, i);
  6.     LOCATE , 30: PRINT _ROUND(b)
  7.     IF j MOD 20 = 0 THEN SLEEP
  8.  
  9. FUNCTION Round## (num AS DOUBLE, digits AS LONG) ' Steve's simplified
  10.     Round## = INT(num * 10 ^ digits + .5) / 10 ^ digits
  11.  

This application of your code is used in counting 200 pennies, added one at a time, as in a ledger app. Note it fails when the tallies are at 7-cents, 56-cents, and 81-cents. I tackled this problem some 30+ years ago, when I wrote the ledger part of my office software. Either I accomplished it back then, or I just got darn lucky over 10 years of using it.  I don't have those routines on my present computer, but now I'm curious. I do recall some rounding methods do just as this one you posted. They fix most of the rounding base two to base ten counting errors, but not the famous 7-cents problem.

For all the good-hearted folks here, please note I do not need this solved, so don't bust you humps on my behalf. Rather, take your time to help others, or to further your own projects.

Oh, and if I somehow just did not apply the code function properly, just lets me know.

Pete

Pete

Pete
Title: Re: I don't often work with numbers, but when I do....
Post by: bplus on September 13, 2021, 12:10:48 pm
Ha! looks like you used it correctly, either exponent notation or extra zero's come in out of nowhere and screw up a screen. It's enough to drive you to string math! :)
Title: Re: I don't often work with numbers, but when I do....
Post by: SMcNeill on September 13, 2021, 12:17:29 pm
Ha! looks like you used it correctly, either exponent notation or extra zero's come in out of nowhere and screw up a screen. It's enough to drive you to string math! :)

Or, just keep in mind, floating point numbers are *always* going to be imprecise figures.  If you need to properly count all your pennies, use INTEGER values just like the banks do.

$1.23 = 123 pennies.
Title: Re: I don't often work with numbers, but when I do....
Post by: Pete on September 13, 2021, 01:19:00 pm
I think I did something like this 30 or so years ago...

Code: QB64: [Select]
  1. WIDTH 80, 42
  2. a = .01
  3. FOR i = 1 TO 200000
  4.     fail = a + fail
  5.     b = a * 100 + b * 100
  6.     b = INT(b + .05) / 100
  7.     PRINT i; "  "; b;: LOCATE , 25: PRINT "w/o adj ="; fail
  8.     IF i MOD 40 = 0 THEN SLEEP
  9.     IF INKEY$ = CHR$(27) THEN END
  10.  

Pete
Title: Re: I don't often work with numbers, but when I do....
Post by: Petr on September 13, 2021, 01:31:48 pm
Very interesting. I tried INPUT, it can SINGLE, DOUBLE and _FLOAT, but really _FLOAT values can't be read back in full. The only way to do this is to read all 32 bytes of the FLOAT value using MEM and then compute it via the binary decomposition of these 32 bytes. In fact, this thread gives me the answer to the question why someone in the distant past had a question about how to do this, how to count numeric values from byte values in memory. STR$ converts the FLOAT value as if it were a DOUBLE value, attempting to get this value using _CV (_FLOAT, nr$), where nr$ is 32 bytes obtained using _MEM, cannot be verified for the same reason with the PRINT command ...

Basically, I'd say it's done this because of speed. But it's interesting and important for me to know this (maybe I'll remember when I come across it someday).
Title: Re: I don't often work with numbers, but when I do....
Post by: Pete on September 15, 2021, 07:16:05 pm
PRINT 7! / 100! ' Single is the only one that will display correctly as .07.
PRINT 7 / 100 ' By default, PRINT is double-precision.
PRINT 7& / 100& ' Long
PRINT 7# / 100# ' Double
PRINT 7&& / 100&& ' Integer64
PRINT 7## / 100## ' Float
a = 7 / 100: PRINT a ' Where variable "a" is by default defined as single (!), so the default double of the print statement is negated.

Well isn't that special? So the snippet I posted earlier depends on using only the default single type declaration to work, and that means it is limited to 32,767. So whatever I did some 30 years ago, it wasn't that. Maybe I did go with string math back then.

Pete
Title: Re: I don't often work with numbers, but when I do....
Post by: bplus on September 16, 2021, 02:12:52 pm
Quote
Code: QB64: [Select]
  1. a = 7 / 100: PRINT a ' Where variable "a" is by default defined as single (!), so the default double of the print statement is negated.

Hmm... did we get to the basic problem now? Print defaults to double when not guided by variable types.

Yes
Code: QB64: [Select]
  1. i = 3
  2. a = .01
  3. For j = 1 To 200
  4.     b = b + a
  5.     Print j, Round##(b, i);
  6.     r! = Round##(b, i)
  7.     Locate , 40: Print r!
  8.     If j Mod 20 = 0 Then Sleep
  9.  
  10. Function Round## (num As Double, digits As Long) ' Steve's simplified
  11.     Round## = Int(num * 10 ^ digits + .5) / 10 ^ digits
  12.  
Title: Re: I don't often work with numbers, but when I do....
Post by: Pete on September 16, 2021, 04:43:56 pm
Well it's prettier than mine, but it accomplishes the same. Neat trick to eliminate the computational erroneous digits, by converting back to a SINGLE type variable, but this still means users are limited to totals of 32,767 before the routine is prone to throwing incorrect results. For instance, going ahead penny by penny, the routine as modified below, starting at b = 32767, has this result after 119 additions = 32768.2. The correct result is supposed to be 32768.19, not 32768.2. The routine displays the following...

...
118  32768.18
119  32768.2
119  32768.21

 So again, there is too much limitation for practical use with any of these methods when we have to rely of the reported results using the SINGLE variable type.

Code: QB64: [Select]
  1. _SCREENMOVE 0, 0: WIDTH 80, 42
  2. a = .01
  3. b = 32767
  4. FOR j = 1 TO 120
  5.     b = b + a
  6.     r! = Round##(b, i)
  7.     PRINT j; r!
  8.     ' IF j MOD 40 = 0 THEN SLEEP
  9.  
  10. FUNCTION Round## (num AS DOUBLE, digits AS LONG) ' Steve's simplified
  11.     Round## = INT(num * 10 ^ digits + .5) / 10 ^ digits
Title: Re: I don't often work with numbers, but when I do....
Post by: bplus on September 16, 2021, 05:45:08 pm
Pete what's with 32768? That's integer limit. You need a number of digits where you have i
Code: QB64: [Select]
  1. _ScreenMove 0, 0: Width 80, 42
  2. a = .01
  3. b = 32767
  4. For j = 1 To 120
  5.     b = b + a
  6.     r! = Round##(b, 2)
  7.     Print j; r!
  8.     ' IF j MOD 40 = 0 THEN SLEEP
  9.  
  10. Function Round## (num As Double, digits As Long) ' Steve's simplified
  11.     Round## = Int(num * 10 ^ digits + .5) / 10 ^ digits
  12.  
  13.  

Everyone is yelling for me to come for supper, I might have been rushed here.
Title: Re: I don't often work with numbers, but when I do....
Post by: bplus on September 16, 2021, 07:08:25 pm
Ohppp! see I knew I was rushed missed the missed .19

OK fixed:
Code: QB64: [Select]
  1. _ScreenMove 0, 0: Width 80, 42
  2. a = .01
  3. b = 32767
  4. For j = 1 To 120
  5.     b = b + a
  6.     r! = Round##(b, 2)
  7.     Print j; r!
  8.     ' IF j MOD 40 = 0 THEN SLEEP
  9.  
  10. Function Round## (num As Double, digits As Long) ' Steve's simplified
  11.     Round## = Int(num * 10 ^ digits + .5) / 10 ^ digits
  12.  
Title: Re: I don't often work with numbers, but when I do....
Post by: Pete on September 16, 2021, 09:01:55 pm
The revision did work for 32767, but then I tried a larger number for b, 327670, and messed around with the digits a bit, but no success.

Code: QB64: [Select]
  1. _SCREENMOVE 0, 0: WIDTH 80, 42
  2. a = .01
  3. b = 327670
  4. FOR j = 1 TO 12000
  5.     b = b + a
  6.     r! = Round##(b, 2)
  7.     PRINT j; r!
  8.     IF j MOD 40 = 0 THEN SLEEP
  9.  
  10. FUNCTION Round## (num AS DOUBLE, digits AS LONG) ' Steve's simplified
  11. Round## = INT(num * 10 ^ digits + .5) / 10 ^ digits
Title: Re: I don't often work with numbers, but when I do....
Post by: bplus on September 16, 2021, 09:29:25 pm
Well when you exceed 7 digits you have to go double.

I just figured out why I was calling it simplified, here is the better (I hope) unsimplified:
Code: QB64: [Select]
  1. _ScreenMove 100, 0: Width 80, 42
  2. Dim As Double a, b, c
  3. a = .01
  4. b = 327670.004
  5. c = 327670.005
  6. For j = 1 To 12000
  7.     b = b + a
  8.     c = c + a
  9.     r## = Round##(b, 2) ' round to 2 decimals
  10.     r2## = Round##(c, 2)
  11.     Print j; r##; r2##
  12.     If j Mod 40 = 0 Then Sleep
  13.  
  14. Function Round## (num As Double, digits As Long) 'unsimplified
  15.     Round## = Int((num + .5 * 10 ^ -digits) * 10 ^ digits) / 10 ^ digits
Title: Re: I don't often work with numbers, but when I do....
Post by: bplus on September 16, 2021, 09:35:44 pm
This works with unsimplified too:
Code: QB64: [Select]
  1. _ScreenMove 100, 0: Width 80, 42
  2. a = .01
  3. b = 32767
  4. For j = 1 To 120
  5.     b = b + a
  6.     r = Round##(b, 2)
  7.     Print j; r
  8.     ' IF j MOD 40 = 0 THEN SLEEP
  9.  
  10.  
  11. Function Round## (num As Double, digits As Long) 'unsimplified
  12.     Round## = Int((num + .5 * 10 ^ -digits) * 10 ^ digits) / 10 ^ digits
  13.