-1

I was making a progress bar when I noticed this, Something wierd is happening when incrementing doubles in for loop.

EDIT : I am not asking why floating points are broken !! My issue is that the for loop is iterating one extra time in case 1 and one less time in case 4 !

I have created 4 test cases. Please See the code below :-

#include <iostream>

using namespace std;
 
int main()
{
    cout << endl << "Should End on 0.9 :-" << endl; // Abnormal
    for (double i = 0 ; i < 1 ; i += 0.1) cout << i << endl;
    
    cout << endl << "Should End on 1 :-" << endl;
    for (double i = 0 ; i <= 1 ; i += 0.1) cout << i << endl;
    
    cout << endl << "Should End on 0.99 :-" << endl;
    for (double i = 0 ; i < 1 ; i += 0.01) cout << i << endl;
    
    cout << endl << "Should End on 1 :-" << endl;   // Abnormal
    for (double i = 0 ; i <= 1 ; i += 0.01) cout << i << endl;
}

Output :-

Should End on 0.9 :-
0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1

Should End on 1 :-
0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1

Should End on 0.99 :-
0
0.01
0.02
0.03
0.04
0.05
0.06
0.07
0.08
0.09
0.1
0.11
0.12
0.13
0.14
0.15
0.16
0.17
0.18
0.19
0.2
0.21
0.22
0.23
0.24
0.25
0.26
0.27
0.28
0.29
0.3
0.31
0.32
0.33
0.34
0.35
0.36
0.37
0.38
0.39
0.4
0.41
0.42
0.43
0.44
0.45
0.46
0.47
0.48
0.49
0.5
0.51
0.52
0.53
0.54
0.55
0.56
0.57
0.58
0.59
0.6
0.61
0.62
0.63
0.64
0.65
0.66
0.67
0.68
0.69
0.7
0.71
0.72
0.73
0.74
0.75
0.76
0.77
0.78
0.79
0.8
0.81
0.82
0.83
0.84
0.85
0.86
0.87
0.88
0.89
0.9
0.91
0.92
0.93
0.94
0.95
0.96
0.97
0.98
0.99

Should End on 1 :-
0
0.01
0.02
0.03
0.04
0.05
0.06
0.07
0.08
0.09
0.1
0.11
0.12
0.13
0.14
0.15
0.16
0.17
0.18
0.19
0.2
0.21
0.22
0.23
0.24
0.25
0.26
0.27
0.28
0.29
0.3
0.31
0.32
0.33
0.34
0.35
0.36
0.37
0.38
0.39
0.4
0.41
0.42
0.43
0.44
0.45
0.46
0.47
0.48
0.49
0.5
0.51
0.52
0.53
0.54
0.55
0.56
0.57
0.58
0.59
0.6
0.61
0.62
0.63
0.64
0.65
0.66
0.67
0.68
0.69
0.7
0.71
0.72
0.73
0.74
0.75
0.76
0.77
0.78
0.79
0.8
0.81
0.82
0.83
0.84
0.85
0.86
0.87
0.88
0.89
0.9
0.91
0.92
0.93
0.94
0.95
0.96
0.97
0.98
0.99

[Program finished]

First case should end on 0.9 but it's ending on 1

And 4th case should end on 1 but it's ending on 0.99

2nd and 3rd case are working normally.

Any ideas what's going on here ? What am I doing wrong ?

I am using Clang compiler on aarch64 cpu.

0xB00B
  • 1,598
  • 8
  • 26
  • 2
    Read http://floating-point-gui.de/ – Basile Starynkevitch May 12 '21 at 06:10
  • @Basile Starynkevitch I am not asking why floating points are broken !! My issue is that the for loop is iterating one extra time in case 1 and one less time in case 4 ! – 0xB00B May 12 '21 at 06:28
  • Did you compile your code with `clang -g -O -Wall`? Are you allowed to use the [GDB](https://www.gnu.org/software/gdb/) debugger? Or the [GCC](http://gcc.gnu.org/) compiler as `gcc -Wall -Wextra -O -g` ? Maybe you want to use static analysis tools like [Fluctuat](http://www.lix.polytechnique.fr/Labo/Sylvie.Putot/fluctuat.html) or [Frama-C](http://frama-c.com) -which has a *Frama-C++* variant- or dynamic tools like [Cadna](http://cadna.lip6.fr/) – Basile Starynkevitch May 12 '21 at 06:30
  • You could consider using also the [Bismon](https://github.com/bstarynk/bismon/) static analyzer and look into the [DECODER](https://www.decoder-project.eu/) project. For Frama-C, or Bismon, or Fluctuat, or DECODER contact me by email to `basile.starynkevitch@cea.fr` (I will anwer in a few days). Mention the URL of your question in your email – Basile Starynkevitch May 12 '21 at 06:37
  • Another approach could be to develop your [GCC plugin](https://gcc.gnu.org/onlinedocs/gccint/Plugins.html) to automatically instrument your C++ code. I do recommend spending days on reading about [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) floating point numbers. If that is allowed to you, you could consider *generating* C++ code with tools like [RefPerSys](http://refpersys.org/) or [GPP](https://logological.org/gpp), or generating instrumented machine code with an improved [asmjit](https://asmjit.com/) or with [libgccjit](https://gcc.gnu.org/onlinedocs/jit/) – Basile Starynkevitch May 12 '21 at 06:41
  • 2
    The linked question does not ask "why", but "whether" floating point math is broken, and, yes, you *are* asking that question. Read the other answer. – j6t May 12 '21 at 06:52
  • 5
    I think the dupe needs to be supplemented by [How do I print a double value with full precision using cout?](https://stackoverflow.com/questions/554063/how-do-i-print-a-double-value-with-full-precision-using-cout). The problem is not that the loop would iterate one time too often, or that the loop condition would be buggy, no, `std::cout` acutally **rounds output**. If you print with full precision you will see that the last number is actually `0.99999999999999989`. – Lukas-T May 12 '21 at 07:00
  • Yes I figured that out using a debugger. I used to think that doubles are just 2 ints, one for base number and other for power, seems like I was wrong. – 0xB00B May 12 '21 at 08:19
  • @TanishqBanyal No, you are basically right. Floating point numbers are expressed as `sign * mantissa * pow(2, exponent)` where `sign` is either `+1` or `-1` and `mantissa` and `exponent` are integers. See also [IEEE 754 on wikipedia](https://en.wikipedia.org/wiki/IEEE_754) for more details. – Lukas-T May 12 '21 at 09:15

1 Answers1

0

The answer is that floating points are "broken" - or at least not trusted for exact values.

In loop 1, when it unexpectedly prints 1.0, the actual value is slightly less (0.999...), so the check that it is less than 1 is still true.

In loop 4, when you expect it to print 1.0, the actual value is slightly higher (1.0001 or something), so the check that it is <= 1 fails.

cptFracassa
  • 893
  • 4
  • 14
  • Yes I figured that out using a debugger. I used to think that doubles are just 2 ints, one for base number and other for power, seems like I was wrong. – 0xB00B May 12 '21 at 08:02