core/vnl/vnl_decnum.cxx
Go to the documentation of this file.
00001 // This is core/vnl/vnl_decnum.cxx
00002 #include "vnl_decnum.h"
00003 #include <vcl_cassert.h>
00004 #include <vcl_sstream.h>
00005 
00006 // constructor from (decimal) string.
00007 // Parses the input into (in that order)
00008 // * optional blanks,
00009 // * the sign (could be "+" or "-" or nothing at all),
00010 // * the mantissa, consisting of just decimal digits (at least one),
00011 // * and the exponent (optional, starts with "e", then optionally "+" or "-", then an integer)
00012 // * If the mantissa contains a decimal point, it is ignored (but the exponent is adapted accordingly).
00013 // Alternatively, the input might also be "NaN", "Inf", "+Inf", or "-Inf".
00014 // See also operator>>(vcl_istream& s, vnl_decnum& r).
00015 vnl_decnum::vnl_decnum(vcl_string const& r)
00016 : sign_('+'), data_(""), exp_(0L)
00017 {
00018   long exp = 0L;
00019   char const* p = r.c_str();
00020   while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') ++p;
00021   if (*p == '-') sign_ = '-', ++p;
00022   else if (*p == '+') ++p;
00023   if (*p == 'I' && *++p == 'n' && *++p == 'f') { data_ = "Inf"; }
00024   else if (*p == 'N' && *++p == 'a' && *++p == 'N') { data_ = "NaN"; }
00025   else {
00026     while (*p == '0') ++p;
00027     while (*p >= '0' && *p <= '9') data_.push_back(*p++);
00028     if (*p == '.') {
00029       ++p;
00030       while (*p >= '0' && *p <= '9') { data_.push_back(*p++); --exp; }
00031     }
00032     if (data_ == "") sign_ = ' ';
00033     else if (*p == 'e') {
00034       ++p;
00035       char sign = '+';
00036       if (*p == '-') sign = '-', ++p;
00037       else if (*p == '+') ++p;
00038       while (*p == '0') ++p;
00039       while (*p >= '0' && *p <= '9') exp_ *= 10L, exp_ += (*p-'0'), ++p;
00040       if (sign == '-') exp_ = -exp_;
00041     }
00042     exp_ += exp;
00043     if (sign_ == ' ') exp_ = 0L;
00044   }
00045 #ifdef DEBUG
00046   vcl_cerr << "Leaving vnl_decnum::vnl_decnum(\"" << r << "\") with " << sign_ << data_ << 'e' << exp_ << '\n';
00047 #endif
00048 }
00049 
00050 // constructor from an unsigned long
00051 vnl_decnum::vnl_decnum(unsigned long r)
00052 : sign_('+'), data_(""), exp_(0L)
00053 {
00054   if (r == 0) sign_ = ' ';
00055   else {
00056     while (r) { data_.insert(data_.begin(), '0'+(r%10)); r/=10; }
00057   }
00058 }
00059 
00060 // constructor from a double
00061 vnl_decnum::vnl_decnum(double r)
00062 {
00063 #ifdef DEBUG
00064   vcl_cerr << "vnl_decnum::vnl_decnum(double " << r << ")\n";
00065 #endif
00066   vcl_ostringstream os; os << r;
00067   *this = vnl_decnum(os.str());
00068 }
00069 
00070 // Implicit type conversion to a decimal string
00071 // Also used for ostream output
00072 vnl_decnum::operator vcl_string() const
00073 {
00074   if (data_=="NaN") return "NaN";
00075   if (sign_==' ') return "0"; // even if the exponent would be nonzero
00076   vcl_string r=data_; if (sign_=='-') r.insert(r.begin(), sign_);
00077   if (exp_ == 0) return r;
00078   // if not a plain integer: also print out the exponent:
00079   r.push_back('e');
00080   long exp=exp_; if (exp < 0) { exp = -exp; r.push_back('-'); }
00081   vcl_string e="";
00082   while (exp) {e.insert(e.begin(), '0'+(exp%10)); exp /=10; }
00083   return r+e;
00084 }
00085 
00086 vnl_decnum::operator unsigned long() const
00087 {
00088   if (data_ == "NaN") return 0L;
00089   if (data_ == "Inf") return 0xffffffffu;
00090   unsigned long l = 0L;
00091   for (long i=0; i<long(data_.length())+exp_; ++i) { l *= 10; if (i<long(data_.length())) l += (data_.c_str()[i]-'0'); } // might overflow!!!
00092   return l; // forget the sign
00093 }
00094 
00095 vnl_decnum::operator long() const
00096 {
00097   if (data_ == "NaN") return 0L;
00098   if (data_ == "Inf" && sign_ == '+') return 0x7fffffff;
00099   else if (data_ == "Inf") return -0x7fffffff - 1;
00100   long l = 0L;
00101   for (long i=0; i<long(data_.length())+exp_; ++i) { l *= 10; if (i<long(data_.length())) l += (data_.c_str()[i]-'0'); } // might overflow!!!
00102   return sign_=='-' ? -l : l;
00103 }
00104 
00105 vnl_decnum::operator unsigned int() const
00106 {
00107   if (data_ == "NaN") return 0L;
00108   if (data_ == "Inf") return 0xffffffffu;
00109   unsigned int l = 0;
00110   for (long i=0; i<long(data_.length())+exp_; ++i) { l *= 10; if (i<long(data_.length())) l += (data_.c_str()[i]-'0'); } // might overflow!!!
00111   return l; // forget the sign
00112 }
00113 
00114 vnl_decnum::operator int() const
00115 {
00116   if (data_ == "NaN") return 0L;
00117   if (data_ == "Inf" && sign_ == '+') return 0x7fffffff;
00118   else if (data_ == "Inf") return -0x7fffffff - 1;
00119   int l = 0;
00120   for (long i=0; i<long(data_.length())+exp_; ++i) { l *= 10; if (i<long(data_.length())) l += (data_.c_str()[i]-'0'); } // might overflow!!!
00121   return sign_=='-' ? -l : l;
00122 }
00123 
00124 bool vnl_decnum::operator==(vnl_decnum const& r) const
00125 {
00126   // quick return if exponents are identical or signs are different:
00127   if (sign_!=r.sign()) return false;
00128   else if (data_ == "NaN" || r.data() == "NaN") return false; // NaN equals nothing!
00129   else if (data_ == "Inf" && r.data() == "Inf") return true; // of the same sign, of course
00130   else if (sign_ == ' ') return true; // both are zero
00131   else if (exp_ == r.exp()) return data_==r.data();
00132   else if (exp_ > r.exp()) {
00133     // by adding zeros to data_ while decreasing exp_ until it equals r.exp(),
00134     // both mantissas become comparable:
00135     return add_zeros(data_,exp_-r.exp()) == r.data();
00136   }
00137   else {
00138     // similarly in the other direction:
00139     return add_zeros(r.data(),r.exp()-exp_) == data_;
00140   }
00141 }
00142 
00143 // This is "operator<" for strings.
00144 // The arguments should consist of digits only (interpreted as mantissas with the same exponent).
00145 // The shorter of the two arguments is implicitly zero-padded.
00146 bool vnl_decnum::comp(vcl_string const& a, vcl_string const& b)
00147 {
00148 #ifdef DEBUG
00149   vcl_cerr << "Entering vnl_decnum::comp with " << a << " and " << b << '\n';
00150 #endif
00151   int i, na = int(a.length()), nb = int(b.length()), nc = na < nb ? na : nb;
00152   for (i = 0; i < nc; ++i) {
00153     if (a.c_str()[i] < b.c_str()[i]) return true;
00154     else if (a.c_str()[i] > b.c_str()[i]) return false;
00155   }
00156   for (; i < nb; ++i) { // in case b is longer than a
00157     if ('0' < b.c_str()[i]) return true;
00158   }
00159   return false; // a longer string "a" cannot be strictly smaller than "b"
00160 }
00161 
00162 bool vnl_decnum::operator< (vnl_decnum const& r) const
00163 {
00164 #ifdef DEBUG
00165   vcl_cerr << "Entering vnl_decnum::operator< with " << data_ << " and " << r.data() << '\n';
00166 #endif
00167   vcl_string rs = r.data();
00168   if (data_ == "NaN" || rs == "NaN") return false; // NaN compares to nothing!
00169   else if (operator==(r)) return false;
00170   else if (data_ == "Inf") return sign_ == '-';
00171   else if (rs == "Inf") return r.sign() == '+';
00172 
00173   if (sign_=='-' && r.sign() == '-') return -r < operator-();
00174   else if (sign_=='-') return true;
00175   else if (r.sign() == '-') return false;
00176   else if (sign_==' ') return true;
00177   else if (r.sign() == ' ') return false;
00178   else if (data_.length()+exp_ < rs.length()+r.exp()) return true;
00179   else if (data_.length()+exp_ > rs.length()+r.exp()) return false;
00180   else // at this point, the orders of magnitude are the same
00181     return comp(data_,rs);
00182 }
00183 
00184 // Returns the sum of the two first arguments (interpreted as mantissas with the same exponent).
00185 // Both arguments should consist of digits only.
00186 // The third argument will be the exponent of the result.
00187 vnl_decnum vnl_decnum::plus(vcl_string const& a, vcl_string const& b, long exp)
00188 {
00189 #ifdef DEBUG
00190   vcl_cerr << "Entering vnl_decnum::plus with " << a << " and " << b << '\n';
00191 #endif
00192   vcl_string result = "";
00193   int na=int(a.length()), nb=int(b.length()), carry=0;
00194   for (--na,--nb; na>=0&&nb>=0; --na,--nb) {
00195     char c = a.c_str()[na] + (b.c_str()[nb] - '0') + carry;
00196     if (c > '9') c-=10, carry=1; else carry=0;
00197     result.insert(result.begin(), c);
00198   }
00199   for (; na>=0&&nb<0; --na) {
00200     char c = a.c_str()[na] + carry;
00201     if (c > '9') c-=10, carry=1; else carry=0;
00202     result.insert(result.begin(), c);
00203   }
00204   for (; nb>=0&&na<0; --nb) {
00205     char c = b.c_str()[nb] + carry;
00206     if (c > '9') c-=10, carry=1; else carry=0;
00207     result.insert(result.begin(), c);
00208   }
00209   if (carry) result.insert(result.begin(), '1');
00210   return vnl_decnum('+',result,exp);
00211 }
00212 
00213 // Returns the difference of the two first arguments (interpreted as mantissas with the same exponent).
00214 // Both arguments should consist of digits only
00215 // and the first one should be numerically larger than the second one.
00216 // The third argument will be the exponent of the result.
00217 vnl_decnum vnl_decnum::minus(vcl_string const& a, vcl_string const& b, long exp)
00218 {
00219 #ifdef DEBUG
00220   vcl_cerr << "Entering vnl_decnum::minus with " << a << " and " << b << '\n';
00221 #endif
00222   vcl_string result = "";
00223   int na=int(a.length()), nb=int(b.length()), carry=0;
00224   assert(na>=nb);
00225   for (--na,--nb; na>=0&&nb>=0; --na,--nb) {
00226     char c = a.c_str()[na] - (b.c_str()[nb] - '0') - carry;
00227     if (c < '0') c+=10, carry=1; else carry=0;
00228     result.insert(result.begin(), c);
00229   }
00230   for (; na>=0&&nb<0; --na) {
00231     char c = a.c_str()[na] - carry;
00232     if (c < '0') c+=10, carry=1; else carry=0;
00233     result.insert(result.begin(), c);
00234   }
00235   for (na=0; result.c_str()[na]=='0'; ++na) ;
00236   if (na) result.erase(0, na);
00237   assert(carry==0);
00238   return vnl_decnum('+',result,exp);
00239 }
00240 
00241 vnl_decnum vnl_decnum::operator+(vnl_decnum const& r) const
00242 {
00243 #ifdef DEBUG
00244   vcl_cerr << "Entering vnl_decnum::operator+ with "
00245            << sign_ << data_ << 'e' << exp_ << " and "
00246            << r.sign() << r.data() << 'e' << r.exp() << '\n';
00247 #endif
00248   if (data_ == "NaN") return *this;
00249   else if (r.data() == "NaN") return r;
00250   else if (data_ == "Inf" && r.data() == "Inf") return sign_ == r.sign() ? *this : vnl_decnum("NaN");
00251   else if (data_ == "Inf") return *this;
00252   else if (r.data() == "Inf") return r;
00253 
00254   if (sign_ == ' ') return r;
00255   else if (r.sign() == ' ') return *this;
00256   else if (operator==(-r)) return vnl_decnum(0L);
00257   // by adding zeros to r.data() while decreasing r.exp() until it equals exp_, both mantissas become comparable:
00258   else if (exp_ < r.exp()) { return operator+(vnl_decnum(r.sign(), add_zeros(r.data(),r.exp()-exp_), exp_)); }
00259   else if (exp_ > r.exp()) { return r.operator+(*this); }
00260   else if (sign_ == '-' && r.sign() == '-') return - plus(data_, r.data(), exp_);
00261   else if (sign_ == '-' && operator<(-r)) return - minus(data_, r.data(), exp_);
00262   else if (sign_ == '-') return minus(r.data(), data_, exp_);
00263   else if (r.sign() == '-' && operator>(-r)) return minus(data_, r.data(), exp_);
00264   else if (r.sign() == '-') return - minus(r.data(), data_, exp_);
00265   else return plus(data_, r.data(), exp_);
00266 }
00267 
00268 // Returns the product of the two arguments.
00269 // The first argument should consist of digits only;
00270 // the second argument should be a single digit.
00271 vcl_string vnl_decnum::mult(vcl_string const& a, char b)
00272 {
00273 #ifdef DEBUG
00274   vcl_cerr << "Entering vnl_decnum::mult with " << a << " and " << b << '\n';
00275 #endif
00276   vcl_string result = "";
00277   int na=int(a.length()), carry=0, bb = b-'0';
00278   assert(bb >= 0 && bb <= 9);
00279   for (--na; na>=0; --na) {
00280     int c = (a.c_str()[na]-'0') * bb + carry;
00281     assert(c >= 0 && c <= 99);
00282     carry = c/10; c%=10;
00283     result.insert(result.begin(), '0'+c);
00284   }
00285   if (carry) result.insert(result.begin(), '0'+carry);
00286   return result;
00287 }
00288 
00289 vnl_decnum vnl_decnum::operator*(vnl_decnum const& r) const
00290 {
00291 #ifdef DEBUG
00292   vcl_cerr << "Entering vnl_decnum::operator* with "
00293            << sign_ << data_ << 'e' << exp_ << " and "
00294            << r.sign() << r.data() << 'e' << r.exp() << '\n';
00295 #endif
00296   if (data_ == "NaN") return *this;
00297   else if (r.data() == "NaN") return r;
00298   else if (data_ == "Inf" || r.data() == "Inf")
00299     return sign_ == r.sign()             ? vnl_decnum("+Inf")
00300          : (sign_==' ' || r.sign()==' ') ? vnl_decnum("NaN")
00301          :                                 vnl_decnum("-Inf");
00302 
00303   int sign = (sign_==' '?0:sign_=='-'?-1:1) * (r.sign()==' '?0:r.sign()=='-'?-1:1);
00304   vnl_decnum result(0L);
00305   if (sign == 0) return result;
00306   vcl_string zeros = "";
00307   int na=int(data_.length());
00308   for (--na; na>=0; --na) {
00309     result += vnl_decnum(mult(r.data(), data_.c_str()[na]) + zeros);
00310     zeros.push_back('0');
00311   }
00312   result <<= (exp_ + r.exp());
00313   return (sign==-1) ? -result : result;
00314 }
00315 
00316 // Returns the largest one-significant-digit divisor of the two arguments.
00317 // The largest multiple of b not larger than a is returned in b.
00318 // (I.e.: the product of the original b with the returned divisor.)
00319 // The arguments should consist of digits only
00320 // and the first one should be numerically larger than the second one.
00321 vcl_string vnl_decnum::div(vcl_string const& a, vcl_string& b)
00322 {
00323 #ifdef DEBUG
00324   vcl_cerr << "Entering vnl_decnum::div with " << a << " and " << b << '\n';
00325 #endif
00326   int na=int(a.length()), nb=int(b.length());
00327   assert(na >= nb);
00328   if (comp(a,b)) ++nb;
00329   vcl_string u = "1";
00330   while (nb<na) { b.push_back('0'), u.push_back('0'); ++nb; }
00331   vcl_string c = b;
00332   for (; u[0]<'9'; u[0]++) {
00333     vnl_decnum d = plus(c,b,0L);
00334     if (vnl_decnum(a) < d) { b=c; return u; }
00335     c=d.data();
00336   }
00337   // if we end up here, the quotient must start with 9:
00338   b=c; return u;
00339 }
00340 
00341 vnl_decnum vnl_decnum::operator/(vnl_decnum const& r) const
00342 {
00343 #ifdef DEBUG
00344   vcl_cerr << "Entering vnl_decnum::operator/ with "
00345            << sign_ << data_ << 'e' << exp_ << " and "
00346            << r.sign() << r.data() << 'e' << r.exp() << '\n';
00347 #endif
00348   if (data_ == "NaN") return *this;
00349   else if (r.data() == "NaN") return r;
00350   else if (data_ == "Inf" && r.data() == "Inf") return vnl_decnum("NaN");
00351   else if (r.data() == "Inf")                   return vnl_decnum(0L);
00352   else if (data_ == "Inf")
00353     return sign_ == r.sign() ? vnl_decnum("+Inf")
00354          :                     vnl_decnum("-Inf");
00355   else if (r == 0L)
00356     return sign_==' ' ? vnl_decnum("NaN")
00357          : sign_=='+' ? vnl_decnum("+Inf")
00358          :              vnl_decnum("-Inf");
00359 
00360   if (r == 1L) return *this;
00361   if (operator==(r)) return vnl_decnum('+',"1",0L);
00362   vcl_string a = data_, b = r.data();
00363   int na=int(a.length()), nb=int(b.length());
00364   vnl_decnum result(0L);
00365   while (na > nb || (na == nb && !comp(a,b))) {
00366     vcl_string c = b;
00367     vcl_string d = div(a, c);
00368 #ifdef DEBUG
00369     vcl_cerr << "vnl_decnum::div returns " << d << '\n';
00370 #endif
00371     result += vnl_decnum(d);
00372     vnl_decnum m = vnl_decnum(a) - vnl_decnum(c);
00373     a = m.data(); na=a.length();
00374   }
00375   result <<= (exp_ - r.exp());
00376   int sign = (sign_=='-'?-1:1) * (r.sign()=='-'?-1:1);
00377   return sign==-1 ? -result : result;
00378 }
00379 
00380 vnl_decnum vnl_decnum::operator%(vnl_decnum const& r) const
00381 {
00382 #ifdef DEBUG
00383   vcl_cerr << "Entering vnl_decnum::operator% with "
00384            << sign_ << data_ << 'e' << exp_ << " and "
00385            << r.sign() << r.data() << 'e' << r.exp() << '\n';
00386 #endif
00387   if (r == 0L) return *this;
00388   else if (data_ == "NaN") return *this;
00389   else if (r.data() == "NaN") return r;
00390   else if (r.data() == "Inf") return vnl_decnum("NaN");
00391   else if (data_ == "Inf")    return *this;
00392 
00393   if (r == vnl_decnum("1")) return vnl_decnum("0");
00394   if (operator==(r)) return vnl_decnum("0");
00395   vcl_string a = data_, b = r.data();
00396   int na=int(a.length()), nb=int(b.length());
00397   while (na > nb || (na == nb && !comp(a,b))) {
00398     vcl_string c = b;
00399     vcl_string d = div(a, c);
00400 #ifdef DEBUG
00401     vcl_cerr << "vnl_decnum::div returns " << d << '\n';
00402 #endif
00403     vnl_decnum m = vnl_decnum(a) - vnl_decnum(c);
00404     a = m.data(); na=a.length();
00405   }
00406   if (na==0) return vnl_decnum(0L);
00407   else       return vnl_decnum(sign_,a,exp_);
00408 }
00409 
00410 // See also the constructor from vcl_string.
00411 vcl_istream& operator>>(vcl_istream& s, vnl_decnum& r)
00412 {
00413 #ifdef DEBUG
00414   vcl_cerr << "Entering operator>>(istream,vnl_decnum) with " << r << '\n';
00415 #endif
00416   vcl_string data = "";
00417   int c = ' ';
00418   while (c == ' ' || c == '\t' || c == '\r') c=s.get(); // blank skipping
00419   if (c == -1 || c == '\n') { r = vnl_decnum(0L); return s; } // stop parsing at EOLN or EOF
00420   if (c == '-') { data = "-"; c=s.get(); }
00421   else if (c == '+') c=s.get();
00422   if (c == 'I' && s.get() == 'n' && s.get() == 'f') { data += "Inf"; }
00423   else if (c == 'N' && s.get() == 'a' && s.get() == 'N') { data = "NaN"; }
00424   else {
00425     while (c == '0') c=s.get();
00426     while ((c >= '0' && c <= '9') || c == '.') { data.push_back(c); c=s.get(); }
00427     if (c == 'e') {
00428       data.push_back(c); c=s.get();
00429       if (c == '-' || c == '+') { data.push_back(c); c=s.get(); }
00430       while (c >= '0' && c <= '9') { data.push_back(c); c=s.get(); }
00431     }
00432   }
00433   r = vnl_decnum(data);
00434   if (c > 0) s.putback(c);
00435   return s;
00436 }
00437 
00438 
00439 // Remove all trailing zeros from the mantissa, and  increase the exponent accordingly.
00440 // Return the (thus modified) *this.
00441 vnl_decnum& vnl_decnum::compactify()
00442 {
00443   if (sign_ == ' ' || data_ == "Inf") { exp_ = 0L; return *this; }
00444   unsigned long n = data_.find_last_not_of('0') + 1;
00445   unsigned long l = data_.length();
00446   if (n < l) { // at least one trailing zero is found
00447     exp_ += l-n; data_.erase(n);
00448   }
00449   else if (n > l) { // there are no non-zeros, i.e.: this number is zero
00450     exp_ = 0; data_.clear(); sign_ = ' ';
00451   }
00452   // if n == l, the mantissa did not end in 0, so it is returned unmodified.
00453   return *this;
00454 }