![]() |
|
Simple Addition gone wrong - Printable Version +- QB64 Phoenix Edition (https://qb64phoenix.com/forum) +-- Forum: QB64 Rising (https://qb64phoenix.com/forum/forumdisplay.php?fid=1) +--- Forum: Code and Stuff (https://qb64phoenix.com/forum/forumdisplay.php?fid=3) +---- Forum: Help Me! (https://qb64phoenix.com/forum/forumdisplay.php?fid=10) +---- Thread: Simple Addition gone wrong (/showthread.php?tid=3875) Pages:
1
2
|
Simple Addition gone wrong - Dimster - 08-20-2025 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 = .3061225So 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? RE: Simple Addition gone wrong - bplus - 08-20-2025 Hows it work with double type? If you get desparate try bplus String Math and never run into this crap! RE: Simple Addition gone wrong - ahenry3068 - 08-20-2025 (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.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. RE: Simple Addition gone wrong - SMcNeill - 08-20-2025 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. RE: Simple Addition gone wrong - ahenry3068 - 08-20-2025 (08-20-2025, 11:15 PM)SMcNeill Wrote: My suggestion is to NOT use any floating point value.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). RE: Simple Addition gone wrong - Unseen Machine - 08-21-2025 Code: (Select All)
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 RE: Simple Addition gone wrong - SMcNeill - 08-21-2025 @Unseen Machine Let me see if I can explain it in a simple way that you (and everyone else) can understand. ![]() 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)
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. )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)
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). RE: Simple Addition gone wrong - bplus - 08-21-2025 (08-21-2025, 03:31 AM)Unseen Machine Wrote: And here is how to do it right! Code: (Select All) _Title "StringMath 2025-08-21 test .0000001 adding" ' b+ 2025-08-21Source .BAS and .BM RE: Simple Addition gone wrong - Dimster - 08-21-2025 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. RE: Simple Addition gone wrong - bplus - 08-21-2025 This looks OK to me, as suggested awhile ago: Code: (Select All) DefDbl A-Z |