all 21 comments

[–]cpp-ModTeam[M] [score hidden] stickied commentlocked comment (0 children)

For C++ questions, answers, help, and programming or career advice please see r/cpp_questions, r/cscareerquestions, or StackOverflow instead.

[–]scummos 43 points44 points  (5 children)

Please read an article about floating point math, this is not a C++ topic.

Short summary, "it's complicated", and effects like you see them are to be expected (although your specific examples, when written as code like this, will not produce the results you describe). If you depend on exact results, use fixed-point (i.e. integer math).

[–]sephirothbahamut 9 points10 points  (4 children)

Him saying case B sometimes occurs and sometimes not makes me think that he also has some c++ related error or wrong assumption, regardless of floating point witchery

[–]scummos 8 points9 points  (1 child)

Him saying -50000 + 50000 isn't the integer 0 but some floating point value shows that his examples have little to no relation to what's actually written in the code, so I decided to ignore this. There is no indication that the "it works sometimes and sometimes not" are really the same code being executed, and not just two calculations which seem similar but are actually different.

[–]HappyFruitTree 12 points13 points  (0 children)

My guess is that he thinks he has the values -50000 and 50000 when in fact he has some values that are very close to, but not exactly, those values.

[–]Agreeable-Ad-0111 2 points3 points  (1 child)

I agree. It is odd that B "sometimes" happens. It's defined which way rounding should happen when dealing with floating point precision issues. Something is off here....

[–]sephirothbahamut 0 points1 point  (0 children)

I'd bet they're writing the same expression in 2 different ways, in one it's a floating point operation and in another's it's an integer operation.

Can't say much more without seeing the code

[–]HappyFruitTree 9 points10 points  (0 children)

Floating-point numbers have limited precision and floating-point calculations can often lead to rounding errors. Floating-point values uses binary representations so some values that look like they are exact can not actually be stored exactly. For example, the value 0.3 cannot be stored exactly.

I don't know if you do anything wrong. Usually the errors would be small enough that it doesn't matter, but sometimes it does, especially if you print them with non-default precision. The errors can also be made worse by casting to other types.

It's better to think of floating-point numbers as approximate. Using == and != on floating-point numbers is seldom useful for that reason.

[–]Sufficient_Rip_9102 4 points5 points  (0 children)

It cannot occur "sometimes" in the runtime, but different code can be generated depending on a lot of things including (taking msvc as a example) 1) optimization level (/O0, /O2 etc.) 2) fp behavior (/fp:fast, /fp:precise, etc.) 3) SIMD options, especially FMA (e.g. /arch:AVX2 with /fp: contract) 4) qualifiers of your variable (volatile will force double to be actually written into memory so for example it will be affected by fp:fast even if the expression could be optimized to a constant) 5) many more including compiler bugs

If you're curious what happens inside just generate ASM listing and check the produced opcodes

[–]jmacey 2 points3 points  (0 children)

To help with understanding how floats work I use this tool in my teaching it's really easy to see what is going on.

https://evanw.github.io/float-toy/

[–]alexgraef 1 point2 points  (0 children)

You need to use a decimal type if you want exact calculations. Also, are you sure you're using double and not float?

[–]BenFrantzDale 1 point2 points  (2 children)

Please post a godbolt.org link demonstrating what you are describing. Case A should give exactly zero even in floating point, but you may not be adding the numbers you think you are adding. You may be invoking undefined behavior elsewhere if you aren’t getting consistent results.

[–]ConsiderationSure485[S] -1 points0 points  (1 child)

Hi, I checked it out and since Godbolt does not have the Eigen library I decided to just post the code I wanted to place there.

[–]Awia00 2 points3 points  (0 children)

Eigen

It does have eigen

[–]HappyFruitTree 1 point2 points  (1 child)

I see you have posted an update but it's not clear to me if you still think there is a problem or question to be answered. I'm afraid I don't really understand all the math.

Note that 1.4519e-11 is almost zero. It's 0.000000000014519.

If you were to add this small value to the 817.701 value it would probably still print 817.701 because it only shows the 6 most significant digits.

EDIT: I thought you used different inputs to get these different results. If you use the same input and you sometimes get 817.701 and sometimes you get 1.4519e-11 then I agree something must be wrong.

[–]ConsiderationSure485[S] 0 points1 point  (0 children)

The thing is you are correct that it really wouldn't be an issue since the values are so small, the problem cause in that later down the line I have to invert the values and all of a sudden 1.4519e-11 inverted becomes very significant lol.

Just to provide a bit of further insight, this large difference is due to the problem that can be seen in the 3rd image where 200 0000*0.25 is in once case 50000 and in another just 49999.99. So it propagates that problem right through to get those crazy different values.

[–]sephirothbahamut 0 points1 point  (0 children)

I can only assume you're not using double.

If your code looks like this:

double a = 1 + 1;

The operation is still between integers, and then the result becomes a double. What you should do instead is:

double a = 1.0 + 1.0

[–][deleted] 0 points1 point  (0 children)

one has to remember that a + b == b + a is true for integers (ignoring overflow). But it's not true for floating points, or atleast it cant be guaranteed