views:

249

answers:

4

How do i determine the first n digits of an exponentiation (ab).

eg: for a = 12, b = 13 & n = 4, the first 4 digits are 1069.
+1  A: 

For this case - with magic numbers 12,13,4 in place:

#include <sstream>
#include <iomanip>
#include <cmath>

double a = 12;
int b = 13;
double result = std::pow(a,b);

std::stringstream strVal;
strVal.setf( ios::fixed, ios::floatfield );
strVal << result;
std::string output(strVal.str().substr(0,4));

output = "1069"

std::stringstream intStr(output);
int intVal;
intStr >> intVal;

intVal = 1069

EDIT: This should work for any combination where result does not overflow double.

Steve Townsend
Is this correct even for huge numbers as long as the result does not overflow `double`? I suspect so, but it would be worth discussing as part of a complete answer.
R..
@R.. - good point, edited to use `long double` and added comment
Steve Townsend
`long double` is likely the same as `double` on Windows. Some analysis of whether the code will be subject to floating point imprecision would be better than just throwing bigger types at it to sweep the issue under the rug.
R..
@R.. - it's as accurate as runtime library's pow function isn't it? Am I missing your point?
Steve Townsend
Unless your system's floating point to decimal conversion is better than any specification requires, it will fail with `n>DECIMAL_DIG`, even if `pow(a,b)` is exactly representable (which will always be the case if `a` is a power of 2). Whether there are other failure cases, I'm not sure. I think it's fine as long as `n` is significantly smaller than `DECIMAL_DIG`.
R..
@R.. - thanks - on Windows you can optionally use SSE2 to support pow. Not sure if that affects the accuracy, or just performance.
Steve Townsend
Sure. BTW, "What are the first 100 digits of 2^10000?" is a perfectly reasonable question where the `pow(2,10000)` would work (for 80-bit or higher `long double`) but where the standard library might not be able to print 100 accurate decimal digits. For what it's worth, glibc will do it correctly and MSVCRT will not.
R..
+1  A: 

The easiest way programatically to do this is to use a stringstream to convert the result of the exponentation to a string and then take the n most significant (i.e. left) characters.

if you want a way without strings then this will work:

#include <iostream>
#include <sstream>
#include <math.h>

using namespace std;

double nDigExp( double a, double b, int n )
{
    stringstream ss;
    ss.setf( ios::fixed, ios::floatfield );
    ss << pow(a,b);
    double ret;
    for ( int i = 0; i < n; ++i) ret = (10 * ret) + (ss.get() - '0'); // Yeuch!!
    return ret;
}

int main( )
{
    double result = nDigExp( 12, 13, 4 );

    cout << result << endl;

    return 0;
}

But it's hardly the most elegent code. I'm sure you can improve it.

Robin Welch
+13  A: 
Alexey Malistov
+1 for an algorithmic solution instead of the iffy float-printing solutions..
R..
+2  A: 

Another solution, using log10:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(int argc, char **argv) {
       int a = 12;
       int b = 13;
       int n = 4;
       double x, y;

       x = b * log10(a);
       y = floor(pow(10, x - floor(x) + n - 1));
       printf("Result: %d\n", (int)y);

       return EXIT_SUCCESS;
}
RC