vnl_decnum.cxx
Go to the documentation of this file.
1 // This is core/vnl/vnl_decnum.cxx
2 #include <sstream>
3 #include <cassert>
4 #include "vnl_decnum.h"
5 
6 // constructor from (decimal) string.
7 // Parses the input into (in that order)
8 // * optional blanks,
9 // * the sign (could be "+" or "-" or nothing at all),
10 // * the mantissa, consisting of just decimal digits (at least one),
11 // * and the exponent (optional, starts with "e", then optionally "+" or "-", then an integer)
12 // * If the mantissa contains a decimal point, it is ignored (but the exponent is adapted accordingly).
13 // Alternatively, the input might also be "NaN", "Inf", "+Inf", or "-Inf".
14 // See also operator>>(std::istream& s, vnl_decnum& r).
15 vnl_decnum::vnl_decnum(std::string const& r)
16 : sign_('+'), data_(""), exp_(0L)
17 {
18  long exp = 0L;
19  char const* p = r.c_str();
20  while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') ++p;
21  if (*p == '-') sign_ = '-', ++p;
22  else if (*p == '+') ++p;
23  if (*p == 'I' && *++p == 'n' && *++p == 'f') { data_ = "Inf"; }
24  else if (*p == 'N' && *++p == 'a' && *++p == 'N') { data_ = "NaN"; }
25  else {
26  while (*p == '0') ++p;
27  while (*p >= '0' && *p <= '9') data_.push_back(*p++);
28  if (*p == '.') {
29  ++p;
30  while (*p >= '0' && *p <= '9') { data_.push_back(*p++); --exp; }
31  }
32  if (data_ == "") sign_ = ' ';
33  else if (*p == 'e') {
34  ++p;
35  char sign = '+';
36  if (*p == '-') sign = '-', ++p;
37  else if (*p == '+') ++p;
38  while (*p == '0') ++p;
39  while (*p >= '0' && *p <= '9') exp_ *= 10L, exp_ += (*p-'0'), ++p;
40  if (sign == '-') exp_ = -exp_;
41  }
42  exp_ += exp;
43  if (sign_ == ' ') exp_ = 0L;
44  }
45 #ifdef DEBUG
46  std::cerr << "Leaving vnl_decnum::vnl_decnum(\"" << r << "\") with " << sign_ << data_ << 'e' << exp_ << '\n';
47 #endif
48 }
49 
50 // constructor from an unsigned long
51 vnl_decnum::vnl_decnum(unsigned long r)
52 : sign_('+'), data_(""), exp_(0L)
53 {
54  if (r == 0) sign_ = ' ';
55  else {
56  while (r) { data_.insert(data_.begin(), '0'+(r%10)); r/=10; }
57  }
58 }
59 
60 // constructor from a double
62 {
63 #ifdef DEBUG
64  std::cerr << "vnl_decnum::vnl_decnum(double " << r << ")\n";
65 #endif
66  std::ostringstream os; os << r;
67  *this = vnl_decnum(os.str());
68 }
69 
70 // Implicit type conversion to a decimal string
71 // Also used for ostream output
72 vnl_decnum::operator std::string() const
73 {
74  if (data_=="NaN") return "NaN";
75  if (sign_==' ') return "0"; // even if the exponent would be nonzero
76  std::string r=data_; if (sign_=='-') r.insert(r.begin(), sign_);
77  if (exp_ == 0) return r;
78  // if not a plain integer: also print out the exponent:
79  r.push_back('e');
80  long exp=exp_; if (exp < 0) { exp = -exp; r.push_back('-'); }
81  std::string e="";
82  while (exp) {e.insert(e.begin(), '0'+(exp%10)); exp /=10; }
83  return r+e;
84 }
85 
86 vnl_decnum::operator unsigned long() const
87 {
88  if (data_ == "NaN") return 0L;
89  if (data_ == "Inf") return 0xffffffffu;
90  unsigned long l = 0L;
91  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!!!
92  return l; // forget the sign
93 }
94 
95 vnl_decnum::operator long() const
96 {
97  if (data_ == "NaN") return 0L;
98  if (data_ == "Inf" && sign_ == '+') return 0x7fffffff;
99  else if (data_ == "Inf") return -0x7fffffff - 1;
100  long l = 0L;
101  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!!!
102  return sign_=='-' ? -l : l;
103 }
104 
105 vnl_decnum::operator unsigned int() const
106 {
107  if (data_ == "NaN") return 0L;
108  if (data_ == "Inf") return 0xffffffffu;
109  unsigned int l = 0;
110  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!!!
111  return l; // forget the sign
112 }
113 
114 vnl_decnum::operator int() const
115 {
116  if (data_ == "NaN") return 0L;
117  if (data_ == "Inf" && sign_ == '+') return 0x7fffffff;
118  else if (data_ == "Inf") return -0x7fffffff - 1;
119  int l = 0;
120  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!!!
121  return sign_=='-' ? -l : l;
122 }
123 
124 bool vnl_decnum::operator==(vnl_decnum const& r) const
125 {
126  // quick return if exponents are identical or signs are different:
127  if (sign_!=r.sign()) return false;
128  else if (data_ == "NaN" || r.data() == "NaN") return false; // NaN equals nothing!
129  else if (data_ == "Inf" && r.data() == "Inf") return true; // of the same sign, of course
130  else if (sign_ == ' ') return true; // both are zero
131  else if (exp_ == r.exp()) return data_==r.data();
132  else if (exp_ > r.exp()) {
133  // by adding zeros to data_ while decreasing exp_ until it equals r.exp(),
134  // both mantissas become comparable:
135  return add_zeros(data_,exp_-r.exp()) == r.data();
136  }
137  else {
138  // similarly in the other direction:
139  return add_zeros(r.data(),r.exp()-exp_) == data_;
140  }
141 }
142 
143 // This is "operator<" for strings.
144 // The arguments should consist of digits only (interpreted as mantissas with the same exponent).
145 // The shorter of the two arguments is implicitly zero-padded.
146 bool vnl_decnum::comp(std::string const& a, std::string const& b)
147 {
148 #ifdef DEBUG
149  std::cerr << "Entering vnl_decnum::comp with " << a << " and " << b << '\n';
150 #endif
151  int i, na = int(a.length()), nb = int(b.length()), nc = na < nb ? na : nb;
152  for (i = 0; i < nc; ++i) {
153  if (a.c_str()[i] < b.c_str()[i]) return true;
154  else if (a.c_str()[i] > b.c_str()[i]) return false;
155  }
156  for (; i < nb; ++i) { // in case b is longer than a
157  if ('0' < b.c_str()[i]) return true;
158  }
159  return false; // a longer string "a" cannot be strictly smaller than "b"
160 }
161 
162 bool vnl_decnum::operator< (vnl_decnum const& r) const
163 {
164 #ifdef DEBUG
165  std::cerr << "Entering vnl_decnum::operator< with " << data_ << " and " << r.data() << '\n';
166 #endif
167  std::string rs = r.data();
168  if (data_ == "NaN" || rs == "NaN") return false; // NaN compares to nothing!
169  else if (operator==(r)) return false;
170  else if (data_ == "Inf") return sign_ == '-';
171  else if (rs == "Inf") return r.sign() == '+';
172 
173  if (sign_=='-' && r.sign() == '-') return -r < operator-();
174  else if (sign_=='-') return true;
175  else if (r.sign() == '-') return false;
176  else if (sign_==' ') return true;
177  else if (r.sign() == ' ') return false;
178  else if (data_.length()+exp_ < rs.length()+r.exp()) return true;
179  else if (data_.length()+exp_ > rs.length()+r.exp()) return false;
180  else // at this point, the orders of magnitude are the same
181  return comp(data_,rs);
182 }
183 
184 // Returns the sum of the two first arguments (interpreted as mantissas with the same exponent).
185 // Both arguments should consist of digits only.
186 // The third argument will be the exponent of the result.
187 vnl_decnum vnl_decnum::plus(std::string const& a, std::string const& b, long exp)
188 {
189 #ifdef DEBUG
190  std::cerr << "Entering vnl_decnum::plus with " << a << " and " << b << '\n';
191 #endif
192  std::string result = "";
193  int na=int(a.length()), nb=int(b.length()), carry=0;
194  for (--na,--nb; na>=0&&nb>=0; --na,--nb) {
195  char c = a.c_str()[na] + (b.c_str()[nb] - '0') + carry;
196  if (c > '9') c-=10, carry=1; else carry=0;
197  result.insert(result.begin(), c);
198  }
199  for (; na>=0&&nb<0; --na) {
200  char c = a.c_str()[na] + carry;
201  if (c > '9') c-=10, carry=1; else carry=0;
202  result.insert(result.begin(), c);
203  }
204  for (; nb>=0&&na<0; --nb) {
205  char c = b.c_str()[nb] + carry;
206  if (c > '9') c-=10, carry=1; else carry=0;
207  result.insert(result.begin(), c);
208  }
209  if (carry) result.insert(result.begin(), '1');
210  return vnl_decnum('+',result,exp);
211 }
212 
213 // Returns the difference of the two first arguments (interpreted as mantissas with the same exponent).
214 // Both arguments should consist of digits only
215 // and the first one should be numerically larger than the second one.
216 // The third argument will be the exponent of the result.
217 vnl_decnum vnl_decnum::minus(std::string const& a, std::string const& b, long exp)
218 {
219 #ifdef DEBUG
220  std::cerr << "Entering vnl_decnum::minus with " << a << " and " << b << '\n';
221 #endif
222  std::string result = "";
223  int na=int(a.length()), nb=int(b.length()), carry=0;
224  assert(na>=nb);
225  for (--na,--nb; na>=0&&nb>=0; --na,--nb) {
226  char c = a.c_str()[na] - (b.c_str()[nb] - '0') - carry;
227  if (c < '0') c+=10, carry=1; else carry=0;
228  result.insert(result.begin(), c);
229  }
230  for (; na>=0&&nb<0; --na) {
231  char c = a.c_str()[na] - carry;
232  if (c < '0') c+=10, carry=1; else carry=0;
233  result.insert(result.begin(), c);
234  }
235  for (na=0; result.c_str()[na]=='0'; ++na) ;
236  if (na) result.erase(0, na);
237  assert(carry==0);
238  return vnl_decnum('+',result,exp);
239 }
240 
242 {
243 #ifdef DEBUG
244  std::cerr << "Entering vnl_decnum::operator+ with "
245  << sign_ << data_ << 'e' << exp_ << " and "
246  << r.sign() << r.data() << 'e' << r.exp() << '\n';
247 #endif
248  if (data_ == "NaN") return *this;
249  else if (r.data() == "NaN") return r;
250  else if (data_ == "Inf" && r.data() == "Inf") return sign_ == r.sign() ? *this : vnl_decnum("NaN");
251  else if (data_ == "Inf") return *this;
252  else if (r.data() == "Inf") return r;
253 
254  if (sign_ == ' ') return r;
255  else if (r.sign() == ' ') return *this;
256  else if (operator==(-r)) return vnl_decnum(0L);
257  // by adding zeros to r.data() while decreasing r.exp() until it equals exp_, both mantissas become comparable:
258  else if (exp_ < r.exp()) { return operator+(vnl_decnum(r.sign(), add_zeros(r.data(),r.exp()-exp_), exp_)); }
259  else if (exp_ > r.exp()) { return r.operator+(*this); }
260  else if (sign_ == '-' && r.sign() == '-') return - plus(data_, r.data(), exp_);
261  else if (sign_ == '-' && operator<(-r)) return - minus(data_, r.data(), exp_);
262  else if (sign_ == '-') return minus(r.data(), data_, exp_);
263  else if (r.sign() == '-' && operator>(-r)) return minus(data_, r.data(), exp_);
264  else if (r.sign() == '-') return - minus(r.data(), data_, exp_);
265  else return plus(data_, r.data(), exp_);
266 }
267 
268 // Returns the product of the two arguments.
269 // The first argument should consist of digits only;
270 // the second argument should be a single digit.
271 std::string vnl_decnum::mult(std::string const& a, char b)
272 {
273 #ifdef DEBUG
274  std::cerr << "Entering vnl_decnum::mult with " << a << " and " << b << '\n';
275 #endif
276  std::string result = "";
277  int na=int(a.length()), carry=0, bb = b-'0';
278  assert(bb >= 0 && bb <= 9);
279  for (--na; na>=0; --na) {
280  int c = (a.c_str()[na]-'0') * bb + carry;
281  assert(c >= 0 && c <= 99);
282  carry = c/10; c%=10;
283  result.insert(result.begin(), '0'+c);
284  }
285  if (carry) result.insert(result.begin(), '0'+carry);
286  return result;
287 }
288 
290 {
291 #ifdef DEBUG
292  std::cerr << "Entering vnl_decnum::operator* with "
293  << sign_ << data_ << 'e' << exp_ << " and "
294  << r.sign() << r.data() << 'e' << r.exp() << '\n';
295 #endif
296  if (data_ == "NaN") return *this;
297  else if (r.data() == "NaN") return r;
298  else if (data_ == "Inf" || r.data() == "Inf")
299  return sign_ == r.sign() ? vnl_decnum("+Inf")
300  : (sign_==' ' || r.sign()==' ') ? vnl_decnum("NaN")
301  : vnl_decnum("-Inf");
302 
303  int sign = (sign_==' '?0:sign_=='-'?-1:1) * (r.sign()==' '?0:r.sign()=='-'?-1:1);
304  vnl_decnum result(0L);
305  if (sign == 0) return result;
306  std::string zeros = "";
307  int na=int(data_.length());
308  for (--na; na>=0; --na) {
309  result += vnl_decnum(mult(r.data(), data_.c_str()[na]) + zeros);
310  zeros.push_back('0');
311  }
312  result <<= (exp_ + r.exp());
313  return (sign==-1) ? -result : result;
314 }
315 
316 // Returns the largest one-significant-digit divisor of the two arguments.
317 // The largest multiple of b not larger than a is returned in b.
318 // (I.e.: the product of the original b with the returned divisor.)
319 // The arguments should consist of digits only
320 // and the first one should be numerically larger than the second one.
321 std::string vnl_decnum::div(std::string const& a, std::string& b)
322 {
323 #ifdef DEBUG
324  std::cerr << "Entering vnl_decnum::div with " << a << " and " << b << '\n';
325 #endif
326  int na=int(a.length()), nb=int(b.length());
327  assert(na >= nb);
328  if (comp(a,b)) ++nb;
329  std::string u = "1";
330  while (nb<na) { b.push_back('0'), u.push_back('0'); ++nb; }
331  std::string c = b;
332  for (; u[0]<'9'; u[0]++) {
333  vnl_decnum d = plus(c,b,0L);
334  if (vnl_decnum(a) < d) { b=c; return u; }
335  c=d.data();
336  }
337  // if we end up here, the quotient must start with 9:
338  b=c; return u;
339 }
340 
342 {
343 #ifdef DEBUG
344  std::cerr << "Entering vnl_decnum::operator/ with "
345  << sign_ << data_ << 'e' << exp_ << " and "
346  << r.sign() << r.data() << 'e' << r.exp() << '\n';
347 #endif
348  if (data_ == "NaN") return *this;
349  else if (r.data() == "NaN") return r;
350  else if (data_ == "Inf" && r.data() == "Inf") return vnl_decnum("NaN");
351  else if (r.data() == "Inf") return vnl_decnum(0L);
352  else if (data_ == "Inf")
353  return sign_ == r.sign() ? vnl_decnum("+Inf")
354  : vnl_decnum("-Inf");
355  else if (r == 0L)
356  return sign_==' ' ? vnl_decnum("NaN")
357  : sign_=='+' ? vnl_decnum("+Inf")
358  : vnl_decnum("-Inf");
359 
360  if (r == 1L) return *this;
361  if (operator==(r)) return vnl_decnum('+',"1",0L);
362  std::string a = data_, b = r.data();
363  int na=int(a.length()), nb=int(b.length());
364  vnl_decnum result(0L);
365  while (na > nb || (na == nb && !comp(a,b))) {
366  std::string c = b;
367  std::string d = div(a, c);
368 #ifdef DEBUG
369  std::cerr << "vnl_decnum::div returns " << d << '\n';
370 #endif
371  result += vnl_decnum(d);
372  vnl_decnum m = vnl_decnum(a) - vnl_decnum(c);
373  a = m.data(); na=a.length();
374  }
375  result <<= (exp_ - r.exp());
376  int sign = (sign_=='-'?-1:1) * (r.sign()=='-'?-1:1);
377  return sign==-1 ? -result : result;
378 }
379 
381 {
382 #ifdef DEBUG
383  std::cerr << "Entering vnl_decnum::operator% with "
384  << sign_ << data_ << 'e' << exp_ << " and "
385  << r.sign() << r.data() << 'e' << r.exp() << '\n';
386 #endif
387  if (r == 0L) return *this;
388  else if (data_ == "NaN") return *this;
389  else if (r.data() == "NaN") return r;
390  else if (r.data() == "Inf") return vnl_decnum("NaN");
391  else if (data_ == "Inf") return *this;
392 
393  if (r == vnl_decnum("1")) return vnl_decnum("0");
394  if (operator==(r)) return vnl_decnum("0");
395  std::string a = data_, b = r.data();
396  int na=int(a.length()), nb=int(b.length());
397  while (na > nb || (na == nb && !comp(a,b))) {
398  std::string c = b;
399  std::string d = div(a, c);
400 #ifdef DEBUG
401  std::cerr << "vnl_decnum::div returns " << d << '\n';
402 #endif
403  vnl_decnum m = vnl_decnum(a) - vnl_decnum(c);
404  a = m.data(); na=a.length();
405  }
406  if (na==0) return vnl_decnum(0L);
407  else return vnl_decnum(sign_,a,exp_);
408 }
409 
410 // See also the constructor from std::string.
411 std::istream& operator>>(std::istream& s, vnl_decnum& r)
412 {
413 #ifdef DEBUG
414  std::cerr << "Entering operator>>(istream,vnl_decnum) with " << r << '\n';
415 #endif
416  std::string data = "";
417  int c = ' ';
418  while (c == ' ' || c == '\t' || c == '\r') c=s.get(); // blank skipping
419  if (c == -1 || c == '\n') { r = vnl_decnum(0L); return s; } // stop parsing at EOLN or EOF
420  if (c == '-') { data = "-"; c=s.get(); }
421  else if (c == '+') c=s.get();
422  if (c == 'I' && s.get() == 'n' && s.get() == 'f') { data += "Inf"; }
423  else if (c == 'N' && s.get() == 'a' && s.get() == 'N') { data = "NaN"; }
424  else {
425  while (c == '0') c=s.get();
426  while ((c >= '0' && c <= '9') || c == '.') { data.push_back(c); c=s.get(); }
427  if (c == 'e') {
428  data.push_back(c); c=s.get();
429  if (c == '-' || c == '+') { data.push_back(c); c=s.get(); }
430  while (c >= '0' && c <= '9') { data.push_back(c); c=s.get(); }
431  }
432  }
433  r = vnl_decnum(data);
434  if (c > 0) s.putback(c);
435  return s;
436 }
437 
438 
439 // Remove all trailing zeros from the mantissa, and increase the exponent accordingly.
440 // Return the (thus modified) *this.
442 {
443  if (sign_ == ' ' || data_ == "Inf") { exp_ = 0L; return *this; }
444  unsigned long n = data_.find_last_not_of('0') + 1;
445  unsigned long l = data_.length();
446  if (n < l) { // at least one trailing zero is found
447  exp_ += l-n; data_.erase(n);
448  }
449  else if (n > l) { // there are no non-zeros, i.e.: this number is zero
450  exp_ = 0; data_.clear(); sign_ = ' ';
451  }
452  // if n == l, the mantissa did not end in 0, so it is returned unmodified.
453  return *this;
454 }
vnl_decnum & compactify()
Remove all trailing zeros from the mantissa, and increase the exponent accordingly.
Definition: vnl_decnum.cxx:441
vnl_decnum operator+() const
Unary plus operator.
Definition: vnl_decnum.h:146
static std::string div(std::string const &, std::string &)
Returns the largest one-significant-digit divisor of the two arguments.
Definition: vnl_decnum.cxx:321
std::string data_
Definition: vnl_decnum.h:89
vnl_decnum operator *(vnl_decnum const &r) const
Product.
Definition: vnl_decnum.cxx:289
bool operator<(vnl_decnum const &) const
Definition: vnl_decnum.cxx:162
long exp_
Definition: vnl_decnum.h:93
#define m
Definition: vnl_vector.h:43
static std::string mult(std::string const &, char)
Returns the product of the two arguments.
Definition: vnl_decnum.cxx:271
vnl_decnum()
Default constructor - creates the number zero.
Definition: vnl_decnum.h:102
std::string data() const
Definition: vnl_decnum.h:98
Infinite precision numbers with decimal arithmetic.
static vnl_decnum plus(std::string const &, std::string const &, long)
Returns the sum of the two first arguments (interpreted as mantissas with the same exponent).
Definition: vnl_decnum.cxx:187
bool operator==(vnl_decnum const &) const
Definition: vnl_decnum.cxx:124
char sign() const
Definition: vnl_decnum.h:99
long exp() const
Definition: vnl_decnum.h:100
static bool comp(std::string const &, std::string const &)
This is "operator<" for strings.
Definition: vnl_decnum.cxx:146
static vnl_decnum minus(std::string const &, std::string const &, long)
Returns the difference of the two first arguments (interpreted as mantissas with the same exponent).
Definition: vnl_decnum.cxx:217
char sign_
Definition: vnl_decnum.h:88
vnl_decnum operator/(vnl_decnum const &r) const
division operator.
Definition: vnl_decnum.cxx:341
vnl_decnum operator%(vnl_decnum const &r) const
modulo operator.
Definition: vnl_decnum.cxx:380
static std::string add_zeros(std::string const &source, unsigned long n)
Definition: vnl_decnum.h:376
VNL_EXPORT std::istream & operator>>(std::istream &s, vnl_decnum &r)
decimal input.
Definition: vnl_decnum.cxx:411
bool operator>(vnl_decnum const &r) const
Definition: vnl_decnum.h:301