Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Simple Addition gone wrong
#1
My code is extremely long and convoluted, so I'm just wondering if anyone has any ideas of where I should look to solve a simple addition problem.

Here is the jest of the math and values which are not adding up correctly

Code: (Select All)
Mean = .3061225
MF = .0000001
CycleValue = Mean + MF
Print CycleValue

So the above code works fine, CycleValue will equal .3061226 but that same addition in my program comes up with CycleValue = .3061225. I'm at a loss to understand why this addition of .0000001 will not take, and not sure where to look in my program that might be the culprit which prevents this simple addition. The value of Mean is dynamic in that there are multiple Cycles but that Mean Factor (MF) stays a constant .0000001. 

Any ideas as to what's going on?
Reply
#2
Hows it work with double type?

If you get desparate try bplus String Math and never run into this crap!
  724  855  599  923  575  468  400  206  147  564  878  823  652  556 bxor cross forever
Reply
#3
(08-20-2025, 08:27 PM)Dimster Wrote: My code is extremely long and convoluted, so I'm just wondering if anyone has any ideas of where I should look to solve a simple addition problem.

Here is the jest of the math and values which are not adding up correctly

Code: (Select All)
Mean = .3061225
MF = .0000001
CycleValue = Mean + MF
Print CycleValue

So the above code works fine, CycleValue will equal .3061226 but that same addition in my program comes up with CycleValue = .3061225. I'm at a loss to understand why this addition of .0000001 will not take, and not sure where to look in my program that might be the culprit which prevents this simple addition. The value of Mean is dynamic in that there are multiple Cycles but that Mean Factor (MF) stays a constant .0000001. 

Any ideas as to what's going on?
bPlus's suggestion is what I would have said too.   But he didn't provide an explanation.     Floating Point Math on machines has a precision limit.   Your result is currently chopped because your exceeding that precision limit for a TYPE SINGLE (The Default type).   

DIM Mean as double
DIM MF as double
DIM CycleValue as DOUBLE 

Do this first.   If that doesn't work then try _FLOAT instead of double.  That has even more precision and range.
Reply
#4
My suggestion is to NOT use any floating point value.

DIM AS _INTEGER64 Mean, MF
Mean = 3061225
MF = 0000001
CycleValue = Mean + MF
Print USING ".#######"; CycleValue / 1000000

Floating point numbers have precision issues inherently built into them. They're built more for speed and approximations than accuracy. If you need accurate numbers, use integer values. Only display the end result as a fractional value; but calculate it as integer.

Banks do this all the time. You don't have $1.23 in your bank account. You have 123 pennies in your bank account. They only print it as $1.23 as a human convenience for you, but internally in the program where it matters, they track, add, and do math on pennies NOT dollars.
Reply
#5
(08-20-2025, 11:15 PM)SMcNeill Wrote: My suggestion is to NOT use any floating point value.

DIM AS _INTEGER64 Mean, MF
Mean = 3061225
MF =    0000001
CycleValue = Mean + MF
Print USING ".#######"; CycleValue / 1000000

Floating point numbers have precision issues inherently built into them.  They're built more for speed and approximations than accuracy.  If you need accurate numbers, use integer values.  Only display the end result as a fractional value; but calculate it as integer.

Banks do this all the time.  You don't have $1.23 in your bank account.  You have 123 pennies in your bank account.  They only print it as $1.23 as a human convenience for you, but internally in the program where it matters, they track, add, and do math on pennies NOT dollars.
   You are of course correct Steve.   But (short of keeping track of people's Money, or making some Auto Course correction on your Self Driving car) I don't see the problem in Using a Higher precision float value if it works in the appliication.    It does keep the code easier to read.   (One of the REASON's BASIC has always used FLOAT as it's default Numeric type).
Reply
#6
Code: (Select All)
FOR I! = 0 TO 1 STEP .0000001
  itot! = itot! + I!
  PRINT "I : ", I!, "I Total : ", itot!

NEXT

It takes a few seconds to run but if you analyse the numbers youll see how adding/incrementing any floating point numbers (and this is in almost any language) will never yield absolute results...I had a similar problem years ago and Galleon tried to explain it to me, all i remember is...two floating point variables that are both .5 when added together do not guarantee the result is 1!

Dont know if that helps!

John
Reply
#7
@Unseen Machine

Let me see if I can explain it in a simple way that you (and everyone else) can understand.   Wink

Let's start with just some plain simple math.  BASE-10, with the values that we're used to seeing and working with in human format.  Let me ask you a simple question:  What is the value of 1 /3?

.33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333...

It's a never-ending fraction which is .333333repeating forever and ever.  Right?

But we don't have limitless memory inside a computer or a calculator, so how do we represent that value??

We chose some arbitrary point of precision and shrug and say, "Eh!  Close enough!"

We might even decide to get technical and write various specifications on that fraction.  "Well let's see... Let's say we choose 8 digits and call it SINGLE precision.  Then if people need more precision, we can create DOUBLE precision and call it 16 digits.  And then if someone REALLY needs more precision, we'll call it DOUBLE DOUBLE and make it 32 digits!"

So now when we ask, "What is 1/3?", the answer depends on the precision we want back from it.
SINGLE would be 0.33333333.
DOUBLE would be 0.3333333333333333.
DOUBLE DOUBLE would be 0.33333333333333333333333333333333.

More digits, so more precision.  But there's still some issues to sort out with our value.  What happens when we multiply by 3?

0.33333333 * 3 = 0.99999999.....  That's not 1!  Our math is off!
0.3333333333333333 * 3 = 0.9999999999999999....  That's closer, but it's still not 1!  Our math is still off!!
and so on...

Since 0.33333333 isn't a direct representation of 1 /3, but instead is a hardware limited approximation based upon precision limits, we're not going to get back the exact value of 1 when we multiple by 3.  So how do we handle that?   We round off values based on that last digit.

Code: (Select All)
Print "0.99999999"; 0.99999999!

Run the above.  See what you get for your result.  I bet it's not 0.99999999...   (In fact, I bet it prints a 1 there for you.  Big Grin)

So now it makes sense.  1 / 3 * 3 = 1
1/3 = 0.33333333
0.33333333 * 3 = 0.99999999
0.99999999 rounds to 1.

But....  If we make that rule our law, then what the heck happens when we have a value that is ACTUALLY 0.33333333 and we multiply it by 3?  Well, of course, it's going to be 1!  

GAH!! That's not right!!   We actually want 0.99999999 as the answer this time!  BUT... How do we toggle the computer/calculator between "Oh, this is how we represent 1 /3", and "this is the actual value"???

Simple answer here -- We don't.

This inherent limitation of imperfect representation of non-terminating fractions is the problem, in a nutshell.  We can't EVER represent 1/3 perfectly in base-10 math, and converting it to a decimal value and then working with it is going to have that imperfection built into it no matter what we try to do.  Decide to round it, you get precision loss.  Decide to truncate it, you get precision loss.  You can't perfectly represent 1 / 3 as a fraction, so when you start making it a fraction and then adding it or multiplying it, you're going to eventually have those imperfections add up on you.



Now, that's talking BASE-10 math as it's a system that we use every day and everyone understands it.  We're taught it in school and I don't think anyone should have any issues whatsoever keeping up with what I've said above.

You can't represent 1 / 3 perfectly in base-10 math.  It's 0.333333333333333333333333333333333333... repeating 3 forever.
You only have so many digits to store in memory, and various memory types have greater limits for less loss of precision, but none of them are infinite.
As you add and subtract these imperfect representations of the value, your loss of precision is going to add up over time.  (Such as in your loop above with itot!.)

Now, here's a different thing to keep in mind also -- How are numbers stored in a computer's memory??

Computers are just on/off electrical toggles.  Numbers are stored with these on/off circuits in BINARY format.

We might write the number 10, and inside our computer's electronics, it's stored as 00001010.

Those are all POWERS OF TWO.   00001010 breaks down to basically (2 ^ 3 + 2 ^ 1) = (8 + 2) = (10).  Each digit to the left of the decimal is a placeholder for a power of 2.

0000000x  <-- here x is 2 ^ 0
000000x0  <-- here x is 2 ^ 1
00000x00  <-- here x is 2 ^ 2
0000x000  <-- here x is 2 ^ 3

Now, seeing this pattern, let me ask you what values exist to the RIGHT of the decimal point.

00000000.x00000000  <-- here x is 2 ^ (-1)
00000000.0x0000000  <-- here x is 2 ^ (-2)
00000000.00x000000  <-- here x is 2 ^ (-3)
00000000.000x00000  <-- here x is 2 ^ (-4)

See that same simple pattern here?  BINARY values can ONLY hold powers of two.

So, with that in mind, tell me what 1 / 10 is, when represented in binary.

It's less than 1/2  (2 ^ -1), so that digit is 0.
It's less than 1/4 (2 ^ -2) so that digit is 0.
It's less than 1/8 (2 ^ -3) so that digit is 0.
It's MORE than 1/16 (2 ^ -4) so that digit is 1.... but we still have more fractions to account for...

If we keep doing the math, what we end up getting is something like:

Approximately 0.00011001100110011001100110011001100110011001100110011001100110...

Notice that this is a non-terminating fraction just like 1/3 is in base-10? You'll NEVER perfectly represent 1 / 10 in base-2. All you can do, is the same as I mentioned above -- choose an arbitrary cut off point and make it your precision limit.

And THIS is why you can have a loop which seems to do *REALLY* simple math, and yet fail at it.

Code: (Select All)
For i = 1 To 10
total = total + .1
Print total
Next

Any dummy should be able to add 0.1 ten times and not have any issues with it. The issue however, is that your PC *can't* do that!! It's not adding 0.1 ten times. It's adding approximately 0.00011001100110011001100110011001100110011001100110011001100110... in binary format 10 times -- and each of those 10 times are *approximate* values based on the limit of precision of your variable types.

If you use SINGLE values, you'll going to see those errors faster and easier than if you use DOUBLE or _FLOAT, as you have less precision and a greater degree of approximation (0.33333333 vs 0.33333333333333333 for example as I mentioned in base-10 above) -- but *THOSE ERRORS ARE GOING TO BE THERE NO MATTER WHICH FLOATING POINT TYPE YOU USE*!!!

(Am I channeling Clippy and screaming like him here? If not, I should be, as this is actually one point that deserves all that excessive emphasis.)

ALL floating point values are going to have issues with precision. Doubles and _Floats have *smaller* problems as they have more digits and accumulate errors slower, but they're still going to have them.

The only way to completely avoid floating point errors in math and precision is... TO AVOID FLOATING POING TYPES!!

Instead of measuring in 0.00000001 inches (or whatever unit you're measuring in), choose to measure it as nano-inches and then you can store the values as INTEGER TYPE numbers -- which don't have the same issue of trouble in representing fractional values.



Floating point values are imprecise by their very nature and are for QUICK math; not ACCURATE math. If you need accurate math, and can't afford the loss of precision (and I'd assume if you're measuring stuff in nano-qualities, you're looking for precision), then use integer types (INTEGER, LONG, INTEGER64) instead of floating point types (SINGLE, DOUBLE, FLOAT).
Reply
#8
(08-21-2025, 03:31 AM)Unseen Machine Wrote:
Code: (Select All)
FOR I! = 0 TO 1 STEP .0000001
  itot! = itot! + I!
  PRINT "I : ", I!, "I Total : ", itot!

NEXT

It takes a few seconds to run but if you analyse the numbers youll see how adding/incrementing any floating point numbers (and this is in almost any language) will never yield absolute results...I had a similar problem years ago and Galleon tried to explain it to me, all i remember is...two floating point variables that are both .5 when added together do not guarantee the result is 1!

Dont know if that helps!

John

And here is how to do it right!
Code: (Select All)
_Title "StringMath 2025-08-21 test .0000001 adding" ' b+ 2025-08-21

$Console:Only
Width 125

I$ = "0"
ITot$ = "0"
Stepper$ = ".0000001"

While LTE(I$, "0.0000100")  ' <<<<  You should get the point after 100 iterations and NOT have to go all the way out to 1.0
    ITot$ = Mr$(I$, "+", ITot$)
    Print "I: "; I$, "I Total: "; ITot$
    I$ = Mr$(I$, "+", Stepper$)
Wend

'$Include: 'StringMath 2025-04-01.bm'

   

Source .BAS and .BM


Attached Files
.zip   StringMath Test add 0000001.zip (Size: 7.14 KB / Downloads: 34)
  724  855  599  923  575  468  400  206  147  564  878  823  652  556 bxor cross forever
Reply
#9
Thanks for all this valuable information. I have tried all your suggestions except bplus string math (and I will be giving that a go as well) but it remains, the same short code I started this thread with will add the two values correctly, while none of the other suggestions have worked on the same math formula in my program. In meantime I'm going to remove a zero from MF value. In my program this does have the addition operational albeit  .0000009 higher than intended but de minimis .

I have to go back and rework your suggestions to be sure I am applying them correctly. Oh the joys of coding.

As always, thanks for the help.
Reply
#10
This looks OK to me, as suggested awhile ago:
Code: (Select All)
DefDbl A-Z
i = 0
stepper = .0000001
While _KeyDown(27) = 0
    Print Using "0.#######  0.#######"; i, itot
    i = i + stepper
    itot = itot + i
    _Limit 2
Wend
  724  855  599  923  575  468  400  206  147  564  878  823  652  556 bxor cross forever
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Nice simple, I hope, question Mad Axeman 4 331 12-20-2025, 09:28 PM
Last Post: SMcNeill
  Trying to create a simple menu CMR 8 1,188 06-18-2025, 06:59 PM
Last Post: CookieOscar
  What is wrong with this for/next loop Helium5793 6 1,120 04-15-2025, 05:11 PM
Last Post: Kernelpanic
  Either I'm doing MID$( wrong or it has a bug TDarcos 4 798 04-13-2025, 11:14 PM
Last Post: TDarcos
  Simple Brick Pattern Fill Question NakedApe 3 892 12-01-2023, 09:37 PM
Last Post: NakedApe

Forum Jump:


Users browsing this thread: 1 Guest(s)