mcchip's blog

By mcchip, history, 4 years ago, In English

I was dividing using the log function in C++ today when I came across an error that I couldn't find an answer for online.

Given an integer n, I wanted to find the floor of log n base 10.

It worked for most numbers besides for some powers of 10. I decided to test increasing powers of 10 from 10^1 to 10^6. I first casted the double results as integers but 10^3 and 10^6 did not give the proper output. Using the floor function on the doubles also gave the same results. I then printed the double results which corrected it, but this won't work for non-perfect powers of 10. I'm a bit confused because I heard that division and multiplication are floating-point error safe.

My code:

int a[6]={10,100,1000,10000,100000,1000000};
    for (int i=0;i<6;i++){
        int x=(int)(log(a[i])/log(10));
        double y=log(a[i])/log(10);
        cout << x << " " << y << " ";
    }

Output: 1 1 2 2 2 3 4 4 5 5 5 6

Does anyone know why this happens and how I can prevent it?

  • Vote: I like it
  • 0
  • Vote: I do not like it

| Write comment?
»
4 years ago, # |
  Vote: I like it 0 Vote: I do not like it

The actual value of the logarithm may be stored as 2.99999999999 rather than 3 because of precision issues. When you output the value of the double it gets rounded to 3 because cout has a set precision of 6. However the value of the logarithm after casting it into an int is 2 since the decimal part has been truncated so the output would be 2.

  • »
    »
    4 years ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    Ah I see; thank you. Do you know if it's possible to be able to distinguish between these precision issue numbers and doubles that are actually below integer values so I can round up and down respectively?

    • »
      »
      »
      4 years ago, # ^ |
        Vote: I like it +6 Vote: I do not like it

      It isn't possible to always get the exact value. For instance, log(10^18) and log(10^18-1) are likely the exact same double value. So you won't be able to get one of them to return 18 and the other to return 17, unless you do something crazy.

      If you are just using it for estimation purposes, you can add 1e-6 before turning into an int and that should help.

      The legit solution is, if you are dealing with integers, to check powers of 10 until you reach that number. Then you don't have to worry about floating point precision. If you need, you can precomp all powers of 10, and find the answer in log(log(n)) with a binary search on these powers.