Synopsis - Cross-Reference
File: /Synopsis/Parsers/IDL/idlfixed.cc1// -*- c++ -*- 2// Package : omniidl 3// idlfixed.h Created on: 2001/01/31 4// Author : Duncan Grisby (dpg1) 5// 6// Copyright (C) 2001 AT&T Laboratories Cambridge 7// 8// This file is part of omniidl. 9// 10// omniidl is free software; you can redistribute it and/or modify it 11// under the terms of the GNU General Public License as published by 12// the Free Software Foundation; either version 2 of the License, or 13// (at your option) any later version. 14// 15// This program is distributed in the hope that it will be useful, 16// but WITHOUT ANY WARRANTY; without even the implied warranty of 17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18// General Public License for more details. 19// 20// You should have received a copy of the GNU General Public License 21// along with this program; if not, write to the Free Software 22// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 23// 02111-1307, USA. 24// 25// Description: 26// 27// Implementation of fixed point type 28 29/* 30 $Log: idlfixed.cc,v $ 31 Revision 1.1.4.2 2005/03/30 23:35:57 dgrisby 32 Another merge from omni4_0_develop. 33 34 Revision 1.1.4.1 2003/03/23 21:01:45 dgrisby 35 Start of omniORB 4.1.x development branch. 36 37 Revision 1.1.2.1 2001/03/13 10:32:12 dpg1 38 Fixed point support. 39 40*/ 41 42#include <idlfixed.h> 43#include <idlutil.h> 44#include <idlerr.h> 45 46#include <string.h> 47 48 49IDL_Fixed::IDL_Fixed() : 50 digits_(0), scale_(0), negative_(0) 51{ 52 memset(val_, 0, OMNI_FIXED_DIGITS); 53} 54 55 56IDL_Fixed::IDL_Fixed(const IDL_Fixed& f) : 57 digits_(f.digits_), scale_(f.scale_), negative_(f.negative_) 58{ 59 memcpy(val_, f.val_, OMNI_FIXED_DIGITS); 60} 61 62IDL_Fixed::IDL_Fixed(const char* s, const char* file, int line) 63{ 64 // Sign (Actually redundant for omniidl. Here for completeness.) 65 if (s[0] == '-') { 66 negative_ = 1; 67 ++s; 68 } 69 else if (s[0] == '+') { 70 negative_ = 0; 71 ++s; 72 } 73 else 74 negative_ = 0; 75 76 // Check there are some digits 77 assert(*s != '\0' && *s != 'd' && *s != 'D'); 78 79 // Skip leading zeros: 80 while (*s == '0') ++s; 81 82 int i, j, unscale = -1; 83 84 // Count digits 85 for (i=0, digits_=0; (s[i] >= '0' && s[i] <= '9') || s[i] == '.'; ++i) { 86 if (s[i] == '.') { 87 assert(unscale == -1); 88 unscale = digits_; 89 } 90 else 91 ++digits_; 92 } 93 if (unscale == -1) 94 unscale = digits_; 95 96 scale_ = digits_ - unscale; 97 98 // Check there is no trailing garbage 99 if (s[i] == 'd' || s[i] == 'D') 100 assert(s[i+1] == '\0'); 101 else 102 assert(s[i] == '\0'); 103 104 --i; // i is now the index of the last digit 105 106 // Truncate if too many digits 107 while (digits_ > OMNI_FIXED_DIGITS && scale_ > 0) { 108 --i; --digits_; --scale_; 109 } 110 111 // Back-up over trailing zeros 112 if (scale_ > 0) { 113 while (s[i] == '0') { 114 --i; --digits_; --scale_; 115 } 116 } 117 118 if (digits_ > OMNI_FIXED_DIGITS) { 119 if (file) { 120 IdlError(file, line, "Fixed point constant has too many digits"); 121 } 122 *this = IDL_Fixed("1"); 123 return; 124 } 125 126 for (j=0; j < digits_; ++j, --i) { 127 if (s[i] == '.') --i; 128 val_[j] = s[i] - '0'; 129 } 130 for (; j < OMNI_FIXED_DIGITS; ++j) 131 val_[j] = 0; 132 133 if (digits_ == 0) negative_ = 0; 134} 135 136IDL_Fixed::IDL_Fixed(const IDL_Octet* val, IDL_UShort digits, 137 IDL_UShort scale, IDL_Boolean negative) 138 : digits_(digits), scale_(scale), negative_(negative) 139{ 140 assert(digits <= OMNI_FIXED_DIGITS); 141 assert(scale <= digits); 142 143 while (digits_ > 0 && scale_ > 0 && val[0] == 0) { 144 digits_--; 145 scale_--; 146 val++; 147 } 148 149 if (digits_ == 0) negative_ = 0; 150 151 memcpy(val_, val, digits_); 152 memset(val_ + digits_, 0, OMNI_FIXED_DIGITS - digits_); 153} 154 155 156IDL_Fixed::~IDL_Fixed() 157{ 158} 159 160 161IDL_Fixed 162IDL_Fixed::truncate(IDL_UShort scale) 163{ 164 if (scale >= scale_) 165 return *this; 166 167 int cut = scale_ - scale; 168 int newscale = scale; 169 170 while (val_[cut] == 0 && newscale > 0) { 171 ++cut; 172 --newscale; 173 } 174 175 return IDL_Fixed(val_ + cut, digits_ - cut, newscale, negative_); 176} 177 178 179 180IDL_Fixed& 181IDL_Fixed::operator=(const IDL_Fixed& f) 182{ 183 digits_ = f.digits_; 184 scale_ = f.scale_; 185 negative_ = f.negative_; 186 memcpy(val_, f.val_, OMNI_FIXED_DIGITS); 187 return *this; 188} 189 190IDL_Fixed 191IDL_Fixed::operator-() const 192{ 193 if (digits_ == 0) return *this; 194 195 IDL_Fixed r(*this); 196 r.negative_ = !r.negative_; 197 return r; 198} 199 200 201char* 202IDL_Fixed::asString() const 203{ 204 int len = digits_ + 1; 205 if (negative_) ++len; // for '-' 206 if (digits_ == scale_) ++len; // for '0' 207 if (scale_ > 0) ++len; // for '.' 208 209 char* r = new char[len]; 210 int i = 0, j; 211 212 if (negative_) r[i++] = '-'; 213 if (digits_ == scale_) r[i++] = '0'; 214 215 for (j=digits_; j; ) { 216 if (j-- == scale_) 217 r[i++] = '.'; 218 r[i++] = val_[j] + '0'; 219 } 220 r[i] = '\0'; 221 return r; 222} 223 224 225 226 227// Functions which do the real arithmetic 228 229 230static int 231absCmp(const IDL_Fixed& a, const IDL_Fixed& b) 232{ 233 int c; 234 c = (a.fixed_digits()-a.fixed_scale()) - (b.fixed_digits()-b.fixed_scale()); 235 if (c) return c; 236 237 int ai, bi; 238 ai = a.fixed_digits() - 1; 239 bi = b.fixed_digits() - 1; 240 241 while (ai >= 0 && bi >= 0) { 242 c = a.val()[ai] - b.val()[bi]; 243 if (c) return c; 244 --ai; --bi; 245 } 246 if (ai >= 0) return 1; 247 if (bi >= 0) return -1; 248 return 0; 249} 250 251 252static IDL_Fixed 253realAdd(const IDL_Fixed& a, const IDL_Fixed& b, IDL_Boolean negative) 254{ 255 int scale, v, carry = 0, ai = 0, bi = 0, wi = 0; 256 IDL_Octet work[OMNI_FIXED_DIGITS * 2]; 257 258 if (a.fixed_scale() > b.fixed_scale()) { 259 scale = a.fixed_scale(); 260 261 while (ai < a.fixed_scale() - b.fixed_scale()) 262 work[wi++] = a.val()[ai++]; 263 } 264 else if (b.fixed_scale() > a.fixed_scale()) { 265 scale = b.fixed_scale(); 266 267 while (bi < b.fixed_scale() - a.fixed_scale()) 268 work[wi++] = b.val()[bi++]; 269 } 270 else 271 scale = a.fixed_scale(); 272 273 while (ai < a.fixed_digits() && bi < b.fixed_digits()) { 274 v = a.val()[ai++] + b.val()[bi++] + carry; 275 if (v > 9) { 276 carry = 1; v -= 10; 277 } 278 else carry = 0; 279 work[wi++] = v; 280 } 281 while (ai < a.fixed_digits()) { 282 v = a.val()[ai++] + carry; 283 if (v > 9) { 284 carry = 1; v -= 10; 285 } 286 else carry = 0; 287 work[wi++] = v; 288 } 289 while (bi < b.fixed_digits()) { 290 v = b.val()[bi++] + carry; 291 if (v > 9) { 292 carry = 1; v -= 10; 293 } 294 else carry = 0; 295 work[wi++] = v; 296 } 297 if (carry) { 298 work[wi++] = carry; 299 } 300 301 IDL_Octet* wp = work; 302 int digits = wi; 303 304 // Truncate or complain if too many digits 305 if (digits > OMNI_FIXED_DIGITS) { 306 if (digits - scale <= OMNI_FIXED_DIGITS) { 307 int chop = digits - OMNI_FIXED_DIGITS; 308 wp += chop; 309 scale -= chop; 310 digits = OMNI_FIXED_DIGITS; 311 } 312 else 313 throw IDL_Fixed::Overflow(); 314 } 315 316 // Strip trailing zeros 317 while (scale > 0 && *wp == 0) { 318 ++wp; --scale; --digits; 319 } 320 return IDL_Fixed(wp, digits, scale, negative); 321} 322 323static IDL_Fixed 324realSub(const IDL_Fixed& a, const IDL_Fixed& b, IDL_Boolean negative) 325{ 326 int scale, v, carry = 0, ai = 0, bi = 0, wi = 0; 327 IDL_Octet work[OMNI_FIXED_DIGITS * 2]; 328 329 if (a.fixed_scale() > b.fixed_scale()) { 330 scale = a.fixed_scale(); 331 332 while (ai < a.fixed_scale() - b.fixed_scale()) 333 work[wi++] = a.val()[ai++]; 334 } 335 else if (b.fixed_scale() > a.fixed_scale()) { 336 scale = b.fixed_scale(); 337 338 while (bi < b.fixed_scale() - a.fixed_scale()) { 339 work[wi++] = 10 - b.val()[bi++] + carry; 340 carry = -1; 341 } 342 } 343 else 344 scale = a.fixed_scale(); 345 346 while (ai < a.fixed_digits() && bi < b.fixed_digits()) { 347 v = a.val()[ai++] - b.val()[bi++] + carry; 348 if (v < 0) { 349 carry = -1; v += 10; 350 } 351 else carry = 0; 352 work[wi++] = v; 353 } 354 while (ai < a.fixed_digits()) { 355 v = a.val()[ai++] + carry; 356 if (v < 0) { 357 carry = -1; v += 10; 358 } 359 else carry = 0; 360 work[wi++] = v; 361 } 362 assert(bi == b.fixed_digits()); 363 assert(carry == 0); 364 365 int digits = wi; 366 IDL_Octet* wp = work; 367 368 // Strip leading zeros 369 while (work[digits-1] == 0 && digits > scale) 370 --digits; 371 372 // Truncate or complain if too many digits 373 if (digits > OMNI_FIXED_DIGITS) { 374 assert(digits - scale <= OMNI_FIXED_DIGITS); 375 376 int chop = digits - OMNI_FIXED_DIGITS; 377 wp += chop; 378 scale -= chop; 379 digits = OMNI_FIXED_DIGITS; 380 } 381 382 // Strip trailing zeros 383 while (scale > 0 && *wp == 0) { 384 ++wp; --scale; --digits; 385 } 386 387 return IDL_Fixed(wp, digits, scale, negative); 388} 389 390 391static IDL_Fixed 392realMul(const IDL_Fixed& a, const IDL_Fixed& b, IDL_Boolean negative) 393{ 394 int ai, bi, wi, digits, scale, v, ad, bd, carry = 0; 395 IDL_Octet work[OMNI_FIXED_DIGITS * 2]; 396 397 memset(work, 0, OMNI_FIXED_DIGITS * 2); 398 399 scale = a.fixed_scale() + b.fixed_scale(); 400 401 for (ai=0, wi=0; ai < a.fixed_digits(); ++ai) { 402 ad = a.val()[ai]; 403 if (ad == 0) continue; 404 405 for (bi=0; bi < b.fixed_digits(); ++bi) { 406 bd = b.val()[bi]; 407 if (bd == 0 && carry == 0) continue; 408 wi = ai + bi; 409 v = work[wi] + ad * bd + carry; 410 carry = v / 10; 411 work[wi] = v % 10; 412 } 413 while (carry) { 414 ++wi; 415 v = work[wi] + carry; 416 carry = v / 10; 417 work[wi] = v % 10; 418 } 419 } 420 digits = wi+1; 421 if (scale > digits) digits = scale; 422 423 // Truncate or complain if too many digits 424 IDL_Octet* wp = work; 425 if (digits > OMNI_FIXED_DIGITS) { 426 if (digits - scale <= OMNI_FIXED_DIGITS) { 427 int chop = digits - OMNI_FIXED_DIGITS; 428 wp += chop; 429 scale -= chop; 430 digits = OMNI_FIXED_DIGITS; 431 } 432 else 433 throw IDL_Fixed::Overflow(); 434 } 435 436 // Strip trailing zeros 437 while (scale > 0 && *wp == 0) { 438 ++wp; --scale; --digits; 439 } 440 441 return IDL_Fixed(wp, digits, scale, negative); 442} 443 444 445static int 446divCmp(const IDL_Octet* av, int ad, const IDL_Octet* bv, int bd, int pos) 447{ 448 int c, ai, bi; 449 450 for (ai = ad-1; ai > pos; --ai) { 451 if (av[ai]) 452 return 1; 453 } 454 ai = pos; 455 bi = bd - 1; 456 assert(ai >= bi); 457 while (bi >= 0) { 458 c = av[ai] - bv[bi]; 459 if (c) return c; 460 --ai; --bi; 461 } 462 return 0; 463} 464 465 466static int 467divDigit(IDL_Octet* av, int ad, const IDL_Octet* bv, int bd, int pos) 468{ 469 int ai, bi, carry, v, count = 0; 470 while (divCmp(av, ad, bv, bd, pos) >= 0) { 471 ++count; 472 carry = 0; 473 474 ai = pos - bd + 1; 475 bi = 0; 476 while (bi < bd) { 477 v = av[ai] - bv[bi] + carry; 478 if (v < 0) { 479 carry = -1; 480 v += 10; 481 } 482 else 483 carry = 0; 484 av[ai] = v; 485 ++ai; 486 ++bi; 487 } 488 while (ai < ad) { 489 v = av[ai] + carry; 490 if (v < 0) { 491 carry = -1; 492 v += 10; 493 } 494 else 495 carry = 0; 496 av[ai] = v; 497 ++ai; 498 } 499 } 500 assert(count < 10); 501 return count; 502} 503 504 505static IDL_Fixed 506realDiv(const IDL_Fixed& a, const IDL_Fixed& b, IDL_Boolean negative) 507{ 508 int i, ai, bi, wi, ri, digits, scale, unscale, v, ad, bd, carry = 0; 509 510 // This division algorithm basically does classic long division. The 511 // numerator, a, is loaded into the top digits of "running". The 512 // divisor, b, is then repeatedly subtracted from the top digits of 513 // "running" until it can no longer be subtracted without becoming 514 // negative. The count of subtractions forms the top digit of the 515 // result. Then the next digit is found by shifting the divisor down 516 // one digit, and repeating, and so on. 517 // 518 // The ugliness all comes because we need to figure out where to put 519 // the decimal point. It would be easy if it wasn't for the fact 520 // that values with scale > digits are not permitted. This means 521 // that if the result is going to be of that form, some initial zero 522 // digits have to be included. 523 524 IDL_Octet work [OMNI_FIXED_DIGITS * 2]; 525 IDL_Octet running[OMNI_FIXED_DIGITS * 2]; 526 527 memset(work, 0, OMNI_FIXED_DIGITS * 2); 528 memset(running, 0, OMNI_FIXED_DIGITS * 2); 529 530 // Skip all leading zeros in a 531 ad = a.fixed_digits(); 532 while (a.val()[ad - 1] == 0) --ad; 533 534 // Set most siginificant digits of running to a's digits 535 ri = OMNI_FIXED_DIGITS * 2 - 1; 536 ai = ad - 1; 537 while (ai >= 0) { 538 running[ri] = a.val()[ai]; 539 --ri; 540 --ai; 541 } 542 543 // Skip all leading zeros in b 544 bd = b.fixed_digits(); 545 while (b.val()[bd - 1] == 0) --bd; 546 547 // unscale = number of digits to left of decimal point in answer 548 unscale = (ad - a.fixed_scale()) - (bd - b.fixed_scale()) + 1; 549 digits = 0; 550 551 // Set some initial zero digits to prevent scale > digits 552 if (unscale < 0) 553 digits = -unscale; 554 555 ri = OMNI_FIXED_DIGITS * 2 - 1; 556 wi = ri - digits; 557 558 // Iterate to work out the result digits 559 while (digits < OMNI_FIXED_DIGITS) { 560 561 // Finish if running is zero 562 for (i=0; i < OMNI_FIXED_DIGITS*2 && running[i] == 0; ++i); 563 if (i == OMNI_FIXED_DIGITS * 2) 564 break; 565 566 work[wi] = divDigit(running, OMNI_FIXED_DIGITS * 2, b.val(), bd, ri); 567 568 if (digits || work[wi]) 569 ++digits; 570 571 --wi; --ri; 572 } 573 wi = OMNI_FIXED_DIGITS * 2 - 1; 574 575 // Skip an initial zero if we weren't expecting one 576 if (unscale >= 0) { 577 while (work[wi] == 0 && unscale > 0) { 578 --wi; --unscale; 579 } 580 } 581 else 582 unscale = 0; 583 584 // Complain if too many digits before decimal point 585 if (unscale > OMNI_FIXED_DIGITS) 586 throw IDL_Fixed::Overflow(); 587 588 // Deal with numbers with trailing zeros before the decimal point 589 if (digits < unscale) 590 digits = unscale; 591 592 IDL_Octet* wp = &work[wi - digits + 1]; 593 594 // Figure out scale 595 scale = digits - unscale; 596 if (digits < scale) 597 digits = scale; 598 599 // Strip trailing zeros 600 while (scale > 0 && *wp == 0) { 601 ++wp; --scale; --digits; 602 } 603 604 return IDL_Fixed(wp, digits, scale, negative); 605} 606 607 608// 609// Operators 610// 611 612IDL_Fixed 613operator+(const IDL_Fixed& a, const IDL_Fixed& b) 614{ 615 if (a.negative() == b.negative()) 616 return realAdd(a, b, a.negative()); 617 618 int cmp = absCmp(a, b); 619 620 if (cmp == 0) // a == b 621 return IDL_Fixed(); 622 else if (cmp > 0) // a > b 623 return realSub(a, b, a.negative()); 624 else 625 return realSub(b, a, b.negative()); 626} 627 628IDL_Fixed 629operator-(const IDL_Fixed& a, const IDL_Fixed& b) 630{ 631 if (a.negative() != b.negative()) 632 return realAdd(a, b, a.negative()); 633 634 int cmp = absCmp(a, b); 635 636 if (cmp == 0) // a == b 637 return IDL_Fixed(); 638 else if (cmp > 0) // a > b 639 return realSub(a, b, a.negative()); 640 else 641 return realSub(b, a, !a.negative()); 642} 643 644IDL_Fixed 645operator*(const IDL_Fixed& a, const IDL_Fixed& b) 646{ 647 if (a.fixed_digits() == 0 || b.fixed_digits() == 0) 648 return IDL_Fixed(); 649 650 if (a.negative() == b.negative()) 651 return realMul(a, b, 0); 652 else 653 return realMul(a, b, 1); 654} 655 656IDL_Fixed 657operator/(const IDL_Fixed& a, const IDL_Fixed& b) 658{ 659 if (b.fixed_digits() == 0) 660 throw IDL_Fixed::DivideByZero(); 661 662 if (a.fixed_digits() == 0) 663 return IDL_Fixed(); 664 665 if (a.negative() == b.negative()) 666 return realDiv(a, b, 0); 667 else 668 return realDiv(a, b, 1); 669} 670