0

I have tried to get the number of digit from double value but it is not working properly. I have tried this:

int main()
{
    double value = 123456.05;
    std::cout<<"number of digit::"<<((int)std::log10(value ) + 1);
}

output::

number of digit::6

How to get exact number of digits? Expected result is 9.

Ilya
  • 4,583
  • 4
  • 26
  • 51
CrazyCoder
  • 772
  • 5
  • 11
  • 31
  • 1
    The problem is, the number of digits may not be the same for two equal double values – polfosol ఠ_ఠ Jun 29 '16 at 05:08
  • Are there always 2 digits after the decimal place? I.e. should value represent something like dollars and cents? – Ben Braun Jun 29 '16 at 05:09
  • 3
    Floating-point values do not have a concept such as "number of digits". – user253751 Jun 29 '16 at 05:12
  • How many digits are in 1/3? – n. m. could be an AI Jun 29 '16 at 05:13
  • @immibis, I understand you are saying this because 3.14 might be represented as 3.140, 3.1400 and so on. But I think the OP is interested in finding the length of a float literal. – abhishek_naik Jun 29 '16 at 05:14
  • 1
    The task is flawed because floating-point values are [represented in base 2](http://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency/3730040#3730040). `1.1` and `1 + 1.0/10` wouldn't show the same number of digits because of rounding errors. You would need to use base 10 decimals, and while several languages have them, I don't think that C++ does. – zneak Jun 29 '16 at 05:15
  • 1
    I don't see how `123456.05` has 9 digits in it unless the decimal counts as a digit. floating point values aren't stored the way you probably think they are. The number of digits before the decimal is pretty easy, the number after is impossible to get right in the way you (likely) want. – Ryan Haining Jun 29 '16 at 05:24
  • 1
    Do you want a different output for `1.23` vs. `1.2300000`? – roeland Jun 29 '16 at 05:36
  • @roelan : yes..1.23 is 4 digit and 1.2300000 is 10 digit. My requirement is like that :( – CrazyCoder Jun 29 '16 at 06:55
  • Your requirements cannot be fulfilled because there is no difference between 1.0, 1.00 and 1.0000000. – n. m. could be an AI Jun 29 '16 at 08:14

2 Answers2

2

Assuming that you only need this to work for double literals, the following will work.

EDIT: Added an equivalent function that works for a subset of doubles. It uses exhaustive search for all reasonable ways to display a double in decimal, there is probably some way to make it more efficient if you need this function to really scream.

    #include <iostream>
    #include <string.h>
    #include <assert.h>
    #include <cmath>

    struct double_literal {
            const char* string_value;
            double double_value;
            size_t num_digits;
    };

    #define DOUBLE_LITERAL(x) {#x, x, strlen(#x)};

size_t num_digits(double value){
        //Double gives around 15 accurate digits usually.
        //Disregarding exponential notation, there are hence only 15 reasonable
        //ways to display value, ranging from 15 places in front of the decimal
        //to 15 behind. Figure out which of these is best in terms of error,
        //and then at the end print out how many digits are needed to display
        //the number by removing unecessary zeros.

        //Routine currently only handles values within these bounds
        //If your value is outside, can scale it with a power of ten before
        //using. Special cases for zero and negative values can also be added.

        double window_stop = std::pow(10.0, 15);
        double window_start = 1 + std::pow(10.0, -15);
        assert(value < window_stop);
        assert(value > window_start);

        size_t best_window = 0;
        double best_error = INFINITY;

        double pow_ten_window = 1;
        for(size_t window = 0; window <= 15; window++, pow_ten_window *= 10){
               double rounded = fmod(
                    std::round(value * pow_ten_window),
                    window_stop
               ) / pow_ten_window;
               double error = std::abs(rounded - value);
               if (error < best_error){
                    best_error = error;
                    best_window = window;
               }
        }

        unsigned long long best_rounding = std::llround(
                fmod(
                        value * std::pow(10.0, best_window),
                        window_stop
                )
        );

        size_t best_digits = std::llround(std::log10(best_rounding) + 1);

        //Representation has an integer part => just figure out if we
        //need a decimal point
        if (best_window > 0){
                best_digits++;
        }

        std::cout << best_window << std::endl;

        return best_digits;

}

int main(int argc, char** argv){
        struct double_literal literal DOUBLE_LITERAL(123456.05);
        std::cout << "number of digit::" << literal.num_digits << std::endl;

        //As a function
        std::cout << "numbr of digit::" << num_digits(literal.double_value);
    }

Using the literal, you can get the value of the literal in multiple forms later in the code.

The function works on non-literals as well, but only for a restricted subset of doubles. See the comments for how to generalize it for the rest.

Ben Braun
  • 418
  • 4
  • 10
  • It is running in all cases but is there any way to write this code in a function. – CrazyCoder Jun 29 '16 at 06:48
  • @CrazyCoder No, this is impossible. Because internal representation of double values `1.0` and `1.00` is the same. This is really strange that you need to catch this difference. If you need a function you can find it in [this answer](http://stackoverflow.com/questions/38090788/how-to-get-the-number-of-digit-in-double-value-in-c/38090879#38090879) – Ilya Jun 29 '16 at 06:51
0

You can convert the double value to string:

double value = 123456.05;
std::string s = std::to_string(value);

After it you need to remove trailing zeroez (because it is possible that s == "123456.050000" now):

s.erase(s.find_last_not_of('0') + 1, std::string::npos);

Than get a number of characters of this string:

std::cout<<"number of digit::"<< s.length();

(In this case you will process "." as digit)

So, the program below returns expected result:

number of digit::9

But this is not perfect for integer values, because "123" will be represented as "123." (one more character at the end). So, for integer values it is better to remove trailing "." before getting of the length:

void print_nb_digits(double value) {
  std::string s = std::to_string(value);
  s.erase(s.find_last_not_of('0') + 1, std::string::npos);
  if (!s.empty() && !std::isdigit(s.back()))
    s.pop_back();
  std::cout<<"number of digit::"<< s.length();
}

double value = 123456.05;
print_nb_digits(value);

In this case the program returns correct result for value = 123456 too:

number of digit::6

Ilya
  • 4,583
  • 4
  • 26
  • 51
  • I think this code will fail in the case when the double value is 123456.00 ? – CrazyCoder Jun 29 '16 at 06:40
  • @CrazyCoder Do you have a way to find a difference between `double a = 123456.00;` and `double b = 123456.0;`? Internal representation of these values will be the same. So the program returns 6 in both cases. Isn't it what you need? – Ilya Jun 29 '16 at 06:43
  • @llya, I have put the value as a example, but the value may be come like 123.45 or 123.00009 or 123.00 or 123.0 anything will come. so, I am not finding the solution in one case.. – CrazyCoder Jun 29 '16 at 06:45
  • @CrazyCoder Ok, but what is the expected result for this example? – Ilya Jun 29 '16 at 06:45
  • It will come 6, when the value 123456.00. – CrazyCoder Jun 29 '16 at 06:50
  • @CrazyCoder Why do you think that this solution is for one case? This program works for any `value`. You need to have `6 ` for `123456.00 ` and it returns `6 `. What is the problem? – Ilya Jun 29 '16 at 06:53
  • @llya the count of digit is 123456.00 is not 6 it is 9. My requirement is this. – CrazyCoder Jun 29 '16 at 06:59
  • @CrazyCoder In this case the only solution is to get this result while compilation (see [here](http://stackoverflow.com/a/38091104/2707359)). – Ilya Jun 29 '16 at 07:07