Here's the code:
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <cmath>
#include <iomanip>
#include <limits>
#include <exception>
const int PRECISION = 100;
typedef boost::multiprecision::number<boost::multiprecision::cpp_dec_float<PRECISION> > arbFloat;
// Prototypes
bool isStringValid(const std::string & str);
bool isNumberValid(const arbFloat & x);
inline std::string resizeArbtoString(const arbFloat & x);
int main(){
arbFloat zeta = 0.0;
std::cout << "\u03b6(s), s = ";
std::string inputStr;
std::getline(std::cin, inputStr);
if(!isStringValid(inputStr)) return 2;
arbFloat input = static_cast<arbFloat>(inputStr);
if(!isNumberValid(input)) return 3;
std::cout << std::setprecision(PRECISION);
int i = 1;
if(input == 0) {
zeta = -0.5;
} else {
std::string preComp, postComp;
std::cout << "Convergence:\n";
do {
preComp = resizeArbtoString(zeta);
zeta += pow(i, -input);
postComp = resizeArbtoString(zeta);
std::cout << '\t' + preComp + '\n';
i++;
} while(preComp != postComp);
}
std::cout << "\n\u03b6(" + inputStr + ") = " << zeta << "\nAfter " << i << " iterations";
return 0;
}
// Check input
bool isStringValid(const std::string & str){
// Check if string contains spaces
if(std::count(str.begin(), str.end(), ' ') > 0){
std::cout << "\nError: Input contains spaces\n";
return false;
}
// Check if string contains multiple .
if(std::count(str.begin(), str.end(), '.') > 1){
std::cout << "\nError: Input contains multiple decimal marks\n";
return false;
}
// Check if NaN or Out of bounds (due to parsing failure)
try{
boost::lexical_cast<arbFloat>(str);
} catch(std::runtime_error){
std::cout << "\nError: Unable to parse (value too large or incorrect number type)\n";
return false;
} catch(...){
std::cout << "\nError: Input is NaN\n";
return false;
}
// Check if intentional NaN
if(boost::icontains(str, "nan")){
std::cout << "\nError: Intentional NaN\n";
return false;
}
return true;
}
// Check number
bool isNumberValid(const arbFloat & x){
// Range check
if(x == std::numeric_limits<arbFloat>::infinity()){
std::cout << "\nError: Out of bounds\n";
return false;
}
if(x == 1){
std::cout << "\nError: Complex Infinity\n";
return false;
}
if(x < 2){
std::cout << "\nError: Does not converge (unsupported)\n";
return false;
}
return true;
}
inline std::string resizeArbtoString(const arbFloat & x){
std::string resizedStr = static_cast<std::string>(x);
resizedStr.resize(PRECISION + 2);
return resizedStr;
}
#Explanation#
This is used to calculate the Riemann Zeta Function for real numbers that are greater than 2 (other than zero). Smaller values will take much longer. This is a result of the algorithm I use for approximating the Riemann Zeta function, not due to implementation. Likewise, larger values are much faster. If a larger value (around 500+) is entered, the precision of 100 digits is exceeded (it returns 1, but there are still digits past all those zeroes).
I am using three Boost libraries: multi-precision, lexical cast, and string algorithms. First I initialize an arbitrary float with the precision of 100
(the value in the code will be manually changed occasionally). I am using function prototypes.
First I define the variable zeta
to be zero. Then I get the user input (example output is below). I then check if it is a valid string that will convert easily to an arbFloat
by using the isStringValid
function. I use a string at first to catch things like multiple periods or spaces (and so on), which result in differing behavior.
I then convert it to an arbFloat
, and check if that number is valid using the isNumberValid
function. Next I set the output precision.
Inside of the if-else statement, I do the calculations. Inside of the else portion, I first convert the initial value of zeta
to a string using the resizeArbtoString
function. The string is being resized to PRECISION + 2
to account for the number before the decimal mark, and the decimal mark itself.
I then do the actual calculations. Next, the resulting value of zeta
is converted to a string using the same technique as before. The do-while loop checks if the strings are the same before and after calculations (if they are, it would result in an endless loop anyways).
Finally the value of zeta
is printed along with the number of iterations.
#Example output#
ζ(s), s = 100
Convergence:
0
1
1.0000000000000000000000000000007888609052210118054117285652827862296732064351090230047702789306640624
1.0000000000000000000000000000007888609052210118073520537827654190672617124639136733859843956171621636
1.0000000000000000000000000000007888609052210118073520537827660413687894985780843877923897736295862226
1.0000000000000000000000000000007888609052210118073520537827660413687896253431444106153299232999067602
1.0000000000000000000000000000007888609052210118073520537827660413687896253431459412620374098062482047
1.0000000000000000000000000000007888609052210118073520537827660413687896253431459412623465788470572268
1.0000000000000000000000000000007888609052210118073520537827660413687896253431459412623465793379665733
1.0000000000000000000000000000007888609052210118073520537827660413687896253431459412623465793379703381
1.0000000000000000000000000000007888609052210118073520537827660413687896253431459412623465793379703382
ζ(100) = 1.000000000000000000000000000000788860905221011807352053782766041368789625343145941262346579337970338
After 12 iterations
RUN SUCCESSFUL (total time: 3s)
Am I doing something inefficiently? How can I improve this code in any way?