idlfixed.cc
来自「编译工具」· CC 代码 · 共 668 行
CC
668 行
// -*- c++ -*-// Package : omniidl// idlfixed.h Created on: 2001/01/31// Author : Duncan Grisby (dpg1)//// Copyright (C) 2001 AT&T Laboratories Cambridge//// This file is part of omniidl.//// omniidl is free software; you can redistribute it and/or modify it// under the terms of the GNU General Public License as published by// the Free Software Foundation; either version 2 of the License, or// (at your option) any later version.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU// General Public License for more details.//// You should have received a copy of the GNU General Public License// along with this program; if not, write to the Free Software// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA// 02111-1307, USA.//// Description:// // Implementation of fixed point type/* $Log: idlfixed.cc,v $ Revision 1.1.2.2 2005/03/29 16:11:07 dgrisby Update omniidl fixed point support with fixes from ORB core. Revision 1.1.2.1 2001/03/13 10:32:12 dpg1 Fixed point support.*/#include <idlfixed.h>#include <idlutil.h>#include <idlerr.h>#include <string.h>IDL_Fixed::IDL_Fixed() : digits_(0), scale_(0), negative_(0){ memset(val_, 0, OMNI_FIXED_DIGITS);}IDL_Fixed::IDL_Fixed(const IDL_Fixed& f) : digits_(f.digits_), scale_(f.scale_), negative_(f.negative_){ memcpy(val_, f.val_, OMNI_FIXED_DIGITS);}IDL_Fixed::IDL_Fixed(const char* s, const char* file, int line){ // Sign (Actually redundant for omniidl. Here for completeness.) if (s[0] == '-') { negative_ = 1; ++s; } else if (s[0] == '+') { negative_ = 0; ++s; } else negative_ = 0; // Check there are some digits assert(*s != '\0' && *s != 'd' && *s != 'D'); // Skip leading zeros: while (*s == '0') ++s; int i, j, unscale = -1; // Count digits for (i=0, digits_=0; (s[i] >= '0' && s[i] <= '9') || s[i] == '.'; ++i) { if (s[i] == '.') { assert(unscale == -1); unscale = digits_; } else ++digits_; } if (unscale == -1) unscale = digits_; scale_ = digits_ - unscale; // Check there is no trailing garbage if (s[i] == 'd' || s[i] == 'D') assert(s[i+1] == '\0'); else assert(s[i] == '\0'); --i; // i is now the index of the last digit // Truncate if too many digits while (digits_ > OMNI_FIXED_DIGITS && scale_ > 0) { --i; --digits_; --scale_; } // Back-up over trailing zeros if (scale_ > 0) { while (s[i] == '0') { --i; --digits_; --scale_; } } if (digits_ > OMNI_FIXED_DIGITS) { if (file) { IdlError(file, line, "Fixed point constant has too many digits"); } *this = IDL_Fixed("1"); return; } for (j=0; j < digits_; ++j, --i) { if (s[i] == '.') --i; val_[j] = s[i] - '0'; } for (; j < OMNI_FIXED_DIGITS; ++j) val_[j] = 0; if (digits_ == 0) negative_ = 0;}IDL_Fixed::IDL_Fixed(const IDL_Octet* val, IDL_UShort digits, IDL_UShort scale, IDL_Boolean negative) : digits_(digits), scale_(scale), negative_(negative){ assert(digits <= OMNI_FIXED_DIGITS); assert(scale <= digits); while (digits_ > 0 && scale_ > 0 && val[0] == 0) { digits_--; scale_--; val++; } if (digits_ == 0) negative_ = 0; memcpy(val_, val, digits_); memset(val_ + digits_, 0, OMNI_FIXED_DIGITS - digits_);}IDL_Fixed::~IDL_Fixed(){}IDL_FixedIDL_Fixed::truncate(IDL_UShort scale){ if (scale >= scale_) return *this; int cut = scale_ - scale; int newscale = scale; while (val_[cut] == 0 && newscale > 0) { ++cut; --newscale; } return IDL_Fixed(val_ + cut, digits_ - cut, newscale, negative_);}IDL_Fixed&IDL_Fixed::operator=(const IDL_Fixed& f){ digits_ = f.digits_; scale_ = f.scale_; negative_ = f.negative_; memcpy(val_, f.val_, OMNI_FIXED_DIGITS); return *this;}IDL_FixedIDL_Fixed::operator-() const{ if (digits_ == 0) return *this; IDL_Fixed r(*this); r.negative_ = !r.negative_; return r;}char*IDL_Fixed::asString() const{ int len = digits_ + 1; if (negative_) ++len; // for '-' if (digits_ == scale_) ++len; // for '0' if (scale_ > 0) ++len; // for '.' char* r = new char[len]; int i = 0, j; if (negative_) r[i++] = '-'; if (digits_ == scale_) r[i++] = '0'; for (j=digits_; j; ) { if (j-- == scale_) r[i++] = '.'; r[i++] = val_[j] + '0'; } r[i] = '\0'; return r;}// Functions which do the real arithmeticstatic intabsCmp(const IDL_Fixed& a, const IDL_Fixed& b){ int c; c = (a.fixed_digits()-a.fixed_scale()) - (b.fixed_digits()-b.fixed_scale()); if (c) return c; int ai, bi; ai = a.fixed_digits() - 1; bi = b.fixed_digits() - 1; while (ai >= 0 && bi >= 0) { c = a.val()[ai] - b.val()[bi]; if (c) return c; --ai; --bi; } if (ai >= 0) return 1; if (bi >= 0) return -1; return 0;}static IDL_FixedrealAdd(const IDL_Fixed& a, const IDL_Fixed& b, IDL_Boolean negative){ int scale, v, carry = 0, ai = 0, bi = 0, wi = 0; IDL_Octet work[OMNI_FIXED_DIGITS * 2]; if (a.fixed_scale() > b.fixed_scale()) { scale = a.fixed_scale(); while (ai < a.fixed_scale() - b.fixed_scale()) work[wi++] = a.val()[ai++]; } else if (b.fixed_scale() > a.fixed_scale()) { scale = b.fixed_scale(); while (bi < b.fixed_scale() - a.fixed_scale()) work[wi++] = b.val()[bi++]; } else scale = a.fixed_scale(); while (ai < a.fixed_digits() && bi < b.fixed_digits()) { v = a.val()[ai++] + b.val()[bi++] + carry; if (v > 9) { carry = 1; v -= 10; } else carry = 0; work[wi++] = v; } while (ai < a.fixed_digits()) { v = a.val()[ai++] + carry; if (v > 9) { carry = 1; v -= 10; } else carry = 0; work[wi++] = v; } while (bi < b.fixed_digits()) { v = b.val()[bi++] + carry; if (v > 9) { carry = 1; v -= 10; } else carry = 0; work[wi++] = v; } if (carry) { work[wi++] = carry; } IDL_Octet* wp = work; int digits = wi; // Truncate or complain if too many digits if (digits > OMNI_FIXED_DIGITS) { if (digits - scale <= OMNI_FIXED_DIGITS) { int chop = digits - OMNI_FIXED_DIGITS; wp += chop; scale -= chop; digits = OMNI_FIXED_DIGITS; } else throw IDL_Fixed::Overflow(); } // Strip trailing zeros while (scale > 0 && *wp == 0) { ++wp; --scale; --digits; } return IDL_Fixed(wp, digits, scale, negative);}static IDL_FixedrealSub(const IDL_Fixed& a, const IDL_Fixed& b, IDL_Boolean negative){ int scale, v, carry = 0, ai = 0, bi = 0, wi = 0; IDL_Octet work[OMNI_FIXED_DIGITS * 2]; if (a.fixed_scale() > b.fixed_scale()) { scale = a.fixed_scale(); while (ai < a.fixed_scale() - b.fixed_scale()) work[wi++] = a.val()[ai++]; } else if (b.fixed_scale() > a.fixed_scale()) { scale = b.fixed_scale(); while (bi < b.fixed_scale() - a.fixed_scale()) { work[wi++] = 10 - b.val()[bi++] + carry; carry = -1; } } else scale = a.fixed_scale(); while (ai < a.fixed_digits() && bi < b.fixed_digits()) { v = a.val()[ai++] - b.val()[bi++] + carry; if (v < 0) { carry = -1; v += 10; } else carry = 0; work[wi++] = v; } while (ai < a.fixed_digits()) { v = a.val()[ai++] + carry; if (v < 0) { carry = -1; v += 10; } else carry = 0; work[wi++] = v; } assert(bi == b.fixed_digits()); assert(carry == 0); int digits = wi; IDL_Octet* wp = work; // Strip leading zeros while (work[digits-1] == 0 && digits > scale) --digits; // Truncate or complain if too many digits if (digits > OMNI_FIXED_DIGITS) { assert(digits - scale <= OMNI_FIXED_DIGITS); int chop = digits - OMNI_FIXED_DIGITS; wp += chop; scale -= chop; digits = OMNI_FIXED_DIGITS; } // Strip trailing zeros while (scale > 0 && *wp == 0) { ++wp; --scale; --digits; } return IDL_Fixed(wp, digits, scale, negative);}static IDL_FixedrealMul(const IDL_Fixed& a, const IDL_Fixed& b, IDL_Boolean negative){ int ai, bi, wi, digits, scale, v, ad, bd, carry = 0; IDL_Octet work[OMNI_FIXED_DIGITS * 2]; memset(work, 0, OMNI_FIXED_DIGITS * 2); scale = a.fixed_scale() + b.fixed_scale(); for (ai=0, wi=0; ai < a.fixed_digits(); ++ai) { ad = a.val()[ai]; if (ad == 0) continue; for (bi=0; bi < b.fixed_digits(); ++bi) { bd = b.val()[bi]; if (bd == 0 && carry == 0) continue; wi = ai + bi; v = work[wi] + ad * bd + carry; carry = v / 10; work[wi] = v % 10; } while (carry) { ++wi; v = work[wi] + carry; carry = v / 10; work[wi] = v % 10; } } digits = wi+1; if (scale > digits) digits = scale; // Truncate or complain if too many digits IDL_Octet* wp = work; if (digits > OMNI_FIXED_DIGITS) { if (digits - scale <= OMNI_FIXED_DIGITS) { int chop = digits - OMNI_FIXED_DIGITS; wp += chop; scale -= chop; digits = OMNI_FIXED_DIGITS; } else throw IDL_Fixed::Overflow(); } // Strip trailing zeros while (scale > 0 && *wp == 0) { ++wp; --scale; --digits; } return IDL_Fixed(wp, digits, scale, negative);}static intdivCmp(const IDL_Octet* av, int ad, const IDL_Octet* bv, int bd, int pos){ int c, ai, bi; for (ai = ad-1; ai > pos; --ai) { if (av[ai]) return 1; } ai = pos; bi = bd - 1; assert(ai >= bi); while (bi >= 0) { c = av[ai] - bv[bi]; if (c) return c; --ai; --bi; } return 0;}static intdivDigit(IDL_Octet* av, int ad, const IDL_Octet* bv, int bd, int pos){ int ai, bi, carry, v, count = 0; while (divCmp(av, ad, bv, bd, pos) >= 0) { ++count; carry = 0; ai = pos - bd + 1; bi = 0; while (bi < bd) { v = av[ai] - bv[bi] + carry; if (v < 0) { carry = -1; v += 10; } else carry = 0; av[ai] = v; ++ai; ++bi; } while (ai < ad) { v = av[ai] + carry; if (v < 0) { carry = -1; v += 10; } else carry = 0; av[ai] = v; ++ai; } } assert(count < 10); return count;}static IDL_FixedrealDiv(const IDL_Fixed& a, const IDL_Fixed& b, IDL_Boolean negative){ int i, ai, bi, wi, ri, digits, scale, unscale, v, ad, bd, carry = 0; // This division algorithm basically does classic long division. The // numerator, a, is loaded into the top digits of "running". The // divisor, b, is then repeatedly subtracted from the top digits of // "running" until it can no longer be subtracted without becoming // negative. The count of subtractions forms the top digit of the // result. Then the next digit is found by shifting the divisor down // one digit, and repeating, and so on. // // The ugliness all comes because we need to figure out where to put // the decimal point. It would be easy if it wasn't for the fact // that values with scale > digits are not permitted. This means // that if the result is going to be of that form, some initial zero // digits have to be included. IDL_Octet work [OMNI_FIXED_DIGITS * 2]; IDL_Octet running[OMNI_FIXED_DIGITS * 2]; memset(work, 0, OMNI_FIXED_DIGITS * 2); memset(running, 0, OMNI_FIXED_DIGITS * 2); // Skip all leading zeros in a ad = a.fixed_digits(); while (a.val()[ad - 1] == 0) --ad; // Set most siginificant digits of running to a's digits ri = OMNI_FIXED_DIGITS * 2 - 1; ai = ad - 1; while (ai >= 0) { running[ri] = a.val()[ai]; --ri; --ai; } // Skip all leading zeros in b bd = b.fixed_digits(); while (b.val()[bd - 1] == 0) --bd; // unscale = number of digits to left of decimal point in answer unscale = (ad - a.fixed_scale()) - (bd - b.fixed_scale()) + 1; digits = 0; // Set some initial zero digits to prevent scale > digits if (unscale < 0) digits = -unscale; ri = OMNI_FIXED_DIGITS * 2 - 1; wi = ri - digits; // Iterate to work out the result digits while (digits < OMNI_FIXED_DIGITS) { // Finish if running is zero for (i=0; i < OMNI_FIXED_DIGITS*2 && running[i] == 0; ++i); if (i == OMNI_FIXED_DIGITS * 2) break; work[wi] = divDigit(running, OMNI_FIXED_DIGITS * 2, b.val(), bd, ri); if (digits || work[wi]) ++digits; --wi; --ri; } wi = OMNI_FIXED_DIGITS * 2 - 1; // Skip an initial zero if we weren't expecting one if (unscale >= 0) { while (work[wi] == 0 && unscale > 0) { --wi; --unscale; } } else unscale = 0; // Complain if too many digits before decimal point if (unscale > OMNI_FIXED_DIGITS) throw IDL_Fixed::Overflow(); // Deal with numbers with trailing zeros before the decimal point if (digits < unscale) digits = unscale; IDL_Octet* wp = &work[wi - digits + 1]; // Figure out scale scale = digits - unscale; if (digits < scale) digits = scale; // Strip trailing zeros while (scale > 0 && *wp == 0) { ++wp; --scale; --digits; } return IDL_Fixed(wp, digits, scale, negative);}//// Operators//IDL_Fixedoperator+(const IDL_Fixed& a, const IDL_Fixed& b){ if (a.negative() == b.negative()) return realAdd(a, b, a.negative()); int cmp = absCmp(a, b); if (cmp == 0) // a == b return IDL_Fixed(); else if (cmp > 0) // a > b return realSub(a, b, a.negative()); else return realSub(b, a, b.negative());}IDL_Fixedoperator-(const IDL_Fixed& a, const IDL_Fixed& b){ if (a.negative() != b.negative()) return realAdd(a, b, a.negative()); int cmp = absCmp(a, b); if (cmp == 0) // a == b return IDL_Fixed(); else if (cmp > 0) // a > b return realSub(a, b, a.negative()); else return realSub(b, a, !a.negative());}IDL_Fixedoperator*(const IDL_Fixed& a, const IDL_Fixed& b){ if (a.fixed_digits() == 0 || b.fixed_digits() == 0) return IDL_Fixed(); if (a.negative() == b.negative()) return realMul(a, b, 0); else return realMul(a, b, 1);}IDL_Fixedoperator/(const IDL_Fixed& a, const IDL_Fixed& b){ if (b.fixed_digits() == 0) throw IDL_Fixed::DivideByZero(); if (a.fixed_digits() == 0) return IDL_Fixed(); if (a.negative() == b.negative()) return realDiv(a, b, 0); else return realDiv(a, b, 1);}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?