>> I decided to do a little challenge we used to give to the students in
>> Bodø in the mid 1990s: to implement a bignumber class and use it to
>> compute the Fibonacci sequence up to a ridiculous length.
>
> I did not really try to find the bug in your code, but I
> wrote a C++ program to print the 100'000th Fibonacci number.
>
> Disclaimer: it's a quick hack: One cannot really change the
> »< long, 1'000'000'000L >« below to something else, unless
> on also edits the »"000000000"« in »to_string«.
>
> Disclaimer: Code was slightly edited between the last test
> and the post, hope not to introduce new bugs.
>
> #include <algorithm>
> #include <cassert>
> #include <climits>
> #include <cmath>
> #include <cstdlib>
> #include <initializer_list>
> #include <iostream>
> #include <ostream>
> #include <string>
>
> using namespace ::std::literals;
>
> /* a vector that will grow on demand */
> template< typename T >
> class vector : public ::std::vector< T >
> { public: using ::std::vector< T >::vector;
>
> /* delete the following function definition when your estimates for
> the number of digits in main are surely large enough (to trade some
> security for speed) */
> T& operator[]( typename vector::size_type const i )
> { while( i >= this->size() )this->push_back( 0 );
> return( *( static_cast< ::std::vector< T >* >( this )))[ i ]; }};
>
> /* a big number */
> /* base must be less than MAX_DIGIT/2, say less than MAX_DIGIT/2-2,
> base must be a power of 10 for the simplistic to_string function
> below to be correct. */
> template< typename DIGIT, DIGIT base >
> class bignum : public vector< DIGIT >
> { public: public: using vector< DIGIT >::vector;
> using digit_type = DIGIT;
> typename ::bignum<DIGIT,base>::size_type length;
>
> /* add h to g giving R,
> h must not be shorter than g,
> R is the result */
> static void sum
> ( ::bignum<DIGIT,base> & g, ::bignum<DIGIT,base> & h,
> ::bignum<DIGIT,base> & R )
> { typename ::bignum<DIGIT,base>::size_type p = 0;
> bool o = false; /* actually the carry bit */
> while( p < g.length )
> { DIGIT const s = o + g[ p ]+ h[ p ];
> R[ p++ ]= s -( o = s > base )* base; }
> while( p < h.length )
> { DIGIT const s = o + h[ p ];
> R[ p++ ]= s -( o = s > base )* base ; }
> if( o )
> { DIGIT const s = o;
> R[ p++ ]= s -( o = s > base )*base; }
> R.length = p; }
>
> static ::std::string to_string( ::bignum<DIGIT,base> & n )
> { ::std::string result; result.reserve( 1000 );
> for( int i = n.length; i > 0; )
> { --i;
> if( i == n.length - 1 )result += ::std::to_string( n[ i ]);
> else
> { ::std::string s = ::std::to_string( n[ i ]);
> char const * p = "000000000" + s.length();
> result += p; result += s; }}
> return result; }
>
> /* set the argument to the number one */
> static void one( ::bignum<DIGIT,base> & x )
> { x[ 0 ]= 1; x.length = 1; }};
>
> int main()
> { using number = bignum< long, 1'000'000'000L >;
> number x( 30'000 ); number::one( x );
> number r( 30'000 ); number::one( r );
> number R( 30'000 ); number::one( R );
> number a;
> for( long i = 2; i <= 100'000; ++i )
> {
> bool print = i == 100'000; /* edit here to specify which values */
> /* you want to be printed. */
> if( print )
> { ::std::string result = number::to_string( r );
> ::std::cout << i << ' ' << result << '\n'; ::std::cout.flush(); }
> if( r.length < x.length )number::sum( r, x, R );
> else number::sum( x, r, R );
> a = ::std::move( R );
> R = ::std::move( x );
> x = ::std::move( r );
> r = ::std::move( a ); }}
Oh. :)
I confirmed, by writing my own little program for that, that you get a
correct result. Although it is for index 999.999 in the sequence, when
one considers fib(0) = 1, fib(1) = 1, fib(2) = 2, fib(3) = 3 and so on.
I'm not sure how your code works, it seems to do strange things, but
here's the expressive C++ dialect code that I cooked up to test it and
show how I would have written that program:
[code]
#include <p/expressive/using_weakly_all.hpp>
#include <algorithm>
#include <iostream>
#include <math.h> // log
#include <vector>
using namespace std;
using Big_int = std::vector<int>; // Each item is a decimal digit.
$procedure add_to( ref_<Big_int> a, ref_<const Big_int> b )
{
$let size_a = n_items_in( a ); $let size_b = n_items_in( b );
$var carry = 0;
for( $each i $in i_up_to( max( size_a, size_b ) ) )
{
if( i >= size_a ) { a.resize( i + 1 ); }
a[i] += carry + ($when( i < size_b ) $use( b[i] ) $default_to(
0 ));
carry = a[i]/10;
a[i] -= 10*carry;
}
if( carry != 0 ) { a.push_back( carry ); }
}
$just
{
$let golden_ratio = 1.6180339887498948482045868343656;
$let n = 100'000;
$let n_digits = static_cast<int>( (n + 10)*log10( golden_ratio ) + 1 );
clog << n_digits << " digits expected.\n";
$var a = Big_int{1}; a.reserve( n_digits );
$var b = Big_int{0}; b.reserve( n_digits );
for( $n_times( n ) )
{
$var next = a;
add_to( next, b );
a = move( b ); b = move( next );
}
for( $each digit $in reverse_span_of( b ) ) { cout << digit; }
cout << endl;
}
[/code]
I did not take the trouble to split the addition loop into parts where
the processing could be specialized for speed; I just used `-O3`. ;-)
This might be the reason that this code is shorter, 43 versus 91 lines,
even though my code looks much more verbose and whitespacey-ish.
Cheers!,
- Alf
PS: One problem with the $n_times macro is that g++ produces a very much
undesired warning “-Wno-unused-but-set-variable”. I just turn it off but
I would like to suppress it for this situation in the code. I tried with
`_Pragma` expressions in the macro but g++ would have none of that
inside a `for` loop head. The macro is currently defined as
`$e::Dummy_reference_consumer $unique_temp_name $in $e::i_up_to( n )`. I
can't think of a way to do that without a range based loop.