-2

I want to use this question to improve a bit in my general understanding of how computer works, since I'll probably never have the chance to study in a profound and deep manner. Sorry in advance if the question is silly and not useful in general, but I prefer to learn in this way.

I am learning c++, I found online a code that implements the Newton-Raphson method for finding the root of a function. The code is pretty simple, as you can see from it, at the beginning it asks for the tolerance required, and if I give a "decent" number it works fine. If instead, when it asks for the tolerance I write something like 1e-600, the program break down immediately and the output is Enter starting value x: Failed to converge after 100 iterations .

The output of failed convergence should be a consequence of running the loop for more than 100 iterations, but this isn't the case since the loop doesn't even start. It looks like the program knows already it won't reach that level of tolerance.

Why does this happen? How can the program write that output even if it didn't try the loop for 100 times?

Edit: It seems that everything meaningless (too small numbers, words) I write when it asks for tolerance produces a pnew=0.25 and then the code runs 100 times and fails.

The code is the following:

#include <iostream>
#include <cmath>
using namespace std;
#define N 100    // Maximum number of iterations


int main() {

    double p, pnew;
    double f, dfdx;
    double tol;
    int i;

    cout << "Enter tolerance: ";
    cin >> tol;
    cout << "Enter starting value x: ";
    cin >> pnew;
    // Main Loop
    for(i=0; i < N; i++){
        p = pnew;
        //Evaluate the function and its derivative
        f = 4*p - cos(p);
        dfdx= 4 + sin(p);
        // The Newton-Raphson step
        pnew = p - f/dfdx;
        // Check for convergence and quit if done
        if(abs(p-pnew) < tol){
            cout << "Root is " << pnew << " to within " << tol << "\n";
            return 0;
        }
    }
    // We reach this point only if the iteration failed to converge
    cerr << "Failed to converge after " << N << " iterations.\n";
    return 1;
}
RenatoRenatoRenato
  • 317
  • 1
  • 3
  • 13
  • 4
    `It looks like the program knows already it won't reach that level of tolerance` That's clearly not the case here. That would be cool though!! – DimChtz Apr 17 '19 at 18:32
  • My advice is to debug on an IDE with an integrated debugger. That way you will be able to see what it does instead of guessing. – drescherjm Apr 17 '19 at 18:33
  • 1
    For a quick debug, add `cout << pnew << '\n';` after `pnew = p - f/dfdx;` and I suspect you will see it run 100 times... Adding `if (!(cin >> tol)) { cerr << "error: invalid input - tol\n"; return 1; }` and similar ***validation*** of EVERY input can also be illuminating.... – David C. Rankin Apr 17 '19 at 18:38
  • 3
    Unrelated: `#define N 100` is high risk. Everything named `N` will be silently replaced by 100 before the program compiles resulting in some truly bizarre error messages if you accidentally reuse `N`. Prefer `constexpr int N = 100;` – user4581301 Apr 17 '19 at 18:38
  • This should help with what @FrançoisAndrieux said: https://en.cppreference.com/w/cpp/types/numeric_limits – drescherjm Apr 17 '19 at 18:38
  • I did like @DavidC.Rankin in his comment and in this case it says "Enter starting value x: 0.25" then run the code and fail to reach converge. It looks like it is exactly like you said, but what's strange is that pnew, between all possible values, takes a value very close to the root which 2.42 – RenatoRenatoRenato Apr 17 '19 at 18:48
  • 1e-600 is a uselessly small number. The Planck length is 1.6e-35 metres, which is 1.6e565 times larger. – molbdnilo Apr 17 '19 at 18:56
  • 1
    @molbdnilo I know it's a stupid number, that's why I tried to put it, to see what happened. Plus I don't get the reference to Planck lengtt, i can invent a unit system in which it is exactly 1e-600, or exactly 1, like it is in Planckian units – RenatoRenatoRenato Apr 17 '19 at 18:58

3 Answers3

3

1e-600 is not representable by most implementations of double. std::cin will fail to convert your input to double and fall into a failed state. This means that, unless you clear the error state, any future std::cin also automatically fails without waiting for user input.

From cppreference (since c++17) :

If extraction fails, zero is written to value and failbit is set. If extraction results in the value too large or too small to fit in value, std::numeric_limits<T>::max() or std::numeric_limits<T>::min() is written and failbit flag is set.

François Andrieux
  • 28,148
  • 6
  • 56
  • 87
  • So if I understand it correctly it should either set tol=0 or set it the the minimum value representable right? Sorry I don't really know much, I wouldn't even know what "failbit is set" means – RenatoRenatoRenato Apr 17 '19 at 18:59
  • 2
    @Runlikehell The streams have error bits, flags that indicate possible error states a stream can be in. If the fail bit is set, it means the stream failed to perform an operation and doesn't trust it's current state. While the stream is in a failed state, it will fail any other input operation. Another commonly used bit is the End Of File (EOF) bit, which is set when trying to read from a stream that has reached the end of it's corresponding input. Again, the stream will fail any further attempt at getting input (for obvious reasons). – François Andrieux Apr 17 '19 at 19:04
  • Thanks, clearer now. Could you look at the edit at the question and maybe guess why it takes the value of pnew=0.25, without even asking me? – RenatoRenatoRenato Apr 17 '19 at 19:12
  • @Runlikehell It's not clear to me how you determined the value of `pnew`. And the behavior depends on which version of c++ you are using. I don't see how `pnew` could be `0.25` with c++17, but with earlier version a failed input operation leaves the value as it was, meaning uninitialized in this case. If this is the case, then you have undefined behavior from reading the value of an uninitialized variable. – François Andrieux Apr 17 '19 at 19:16
  • after I enter the meaningles tolerance, the console writes `Enter starting value x: 0.25` so i assumed It took that value – RenatoRenatoRenato Apr 17 '19 at 19:20
  • @Runlikehell No, that has to be something else. `std::cin` doesn't print anything. Most terminals will echo your input to the shell though, so it looks like it does. Edit : I can't see what would cause that output. Edit 2 : But if you have undefined behavior, anything is possible. – François Andrieux Apr 17 '19 at 19:21
  • another weird thing: if I tell it to print pnew at the very beginning of the loop, after the meaningless tolerance the console writes `Enter starting value x: 0` , but then when he has to print pnew for the first time he prints 0.25. I have always been kinda fascinated by weird machines behaviour, especially when exposed to meaningless things, but if you don't have time or will to understand this one, which is probably not even very interesting, there's no need invest more time answering me. – RenatoRenatoRenato Apr 17 '19 at 19:31
  • @Runlikehell It sounds an awful lot like undefined behavior. Try initializing all your variables and see what happens (the UB should go away). But diving into why UB exhibits itself in one way or another is beyond the scope of this question. – François Andrieux Apr 17 '19 at 19:33
  • Yes, it disappears after initializing pnew. Still curious about why it assigns the value on its own when it's not initialized, but as you said this is not the place to discuss it, thanks. – RenatoRenatoRenato Apr 17 '19 at 19:42
1

As mentioned, 1e-600 is not a valid double value. However, there's more to it than being outside of the range. What's likely happening is that 1 is scanned into tol, and then some portion of e-600 is being scanned into pnew, and that's why it ends immediately, instead of asking for input for pnew.

anonmess
  • 465
  • 3
  • 9
  • I did like @David C. Rankin said in his comment and in this case it says "Enter starting value x: 0.25" then run the code and fail to reach converge. It looks like it is exactly like you said, but what's strange is that pnew, between all possible values, takes a value very close to the root – RenatoRenatoRenato Apr 17 '19 at 18:47
0

Like François said, you cannot exeed 2^64 when you work on an 64bit machine (with corresponding OS) and 2^32 on a 32bit machine, you can use SSE which are 4 32 bytes data used for floating point representation. In your program the function fails at every iteration and skips your test with "if" and so never returns before ending the loop.

Yvain
  • 29
  • 10