note description: "[ Perform arithmetic (+, -, *, /) on real numbers of arbitrary precision. In the case of division, the default precision is 35 digits after the decimal point. This can be changed via set_division_precision. The queries is_integer, is_natural and is_natural1 may be used to check that the value is an integer or natural number. out provides a string represesentation rounded to two decimal places, and precise_out is a string representation of the precise value. The corresponding C# class was the inspiration for this class. ]" author: "Vlad Gerchikov for the Software Engineering Lab at sel@cse.yorku.ca" date: "$April 15, 2008$" revision: "$1.0$" expanded class VALUE inherit COMPARABLE redefine is_equal, default_create, out end NUMERIC redefine is_equal, default_create, out end create make_from_string, make, make_from_int, default_create convert make_from_string ({STRING_8}), as_double: {REAL_64} feature {VALUE} -- private fields s_: STRING_8 divisiondecimalprecision_: INTEGER_32 -- the minimum accuracy of division results; division results will be accurate to AT LEAST this many digits Default_precision: INTEGER_32 = 35 make (s: STRING_8; dv: INTEGER_32) -- create a MONEY_VALUE object from string s require non_void: s /= Void non_empty: not s.is_empty has_correct_format: ensurevalid (s) do divisiondecimalprecision_ := dv s_ := s end clone_me: VALUE local l_s: STRING_8 do create l_s.make_from_string (s_) create Result.make (l_s, divisiondecimalprecision_) end feature {NONE} -- constructors default_create -- create an empty MONEY_VALUE object (it's equivalent to 0) local empty: STRING_8 do divisiondecimalprecision_ := Default_precision empty := "0" s_ := empty.twin ensure then s_.is_equal ("0") end make_from_string (s: STRING_8) -- create a MONEY_VALUE object from string s require non_void: s /= Void non_empty: not s.is_empty has_correct_format: ensurevalid (s) do divisiondecimalprecision_ := Default_precision s_ := s.twin normalize end make_from_int (int: INTEGER_32) -- create a MONEY_VALUE object from integer m local ch: CHARACTER_8 m, n, d: INTEGER_32 neg: BOOLEAN do divisiondecimalprecision_ := Default_precision m := int if m = 0 then create s_.make_from_string ("0") else neg := m < 0 if neg then m := m * -1 end from n := m create s_.make_empty until n = 0 loop d := n \\ 10 n := n // 10 ch := tochar (d) s_.insert_character (ch, 1) end if neg then negate end normalize end end feature -- comparison is_equal (other: VALUE): BOOLEAN -- check whether other is equal to current or not local r1i, r2i, d1, d2: INTEGER_32 r1, r2: VALUE do r1 := Current.clone_me r2 := other.clone_me r1.aligndecimal (r2) r1.alignwhole (r2) Result := True from r1i := 1 r2i := 1 until r1i > r1.s_.count or r2i > r2.s_.count loop d1 := todigit (r1.s_.item (r1i)) d2 := todigit (r2.s_.item (r2i)) if d1 /= d2 then Result := False r1i := r1.s_.count + 1 end r1i := r1i + 1 r2i := r2i + 1 end end is_less alias "<" (other: VALUE): BOOLEAN -- whether current is less than other local r1i, r2i, d1, d2: INTEGER_32 r1, r2: VALUE do r1 := Current.clone_me r2 := other.clone_me if r1.s_.item (1) = '-' and r2.s_.item (1) /= '-' then Result := True elseif r1.s_.item (1) /= '-' and r2.s_.item (1) = '-' then Result := False elseif r1.s_.item (1) = '-' and r2.s_.item (1) = '-' then r1.negate r2.negate Result := r2 < r1 else r1.aligndecimal (r2) r1.alignwhole (r2) Result := False from r1i := 1 r2i := 1 until r1i > r1.s_.count or r2i > r2.s_.count loop d1 := todigit (r1.s_.item (r1i)) d2 := todigit (r2.s_.item (r2i)) if d1 < d2 then Result := True r1i := r1.s_.count + 1 elseif d1 > d2 then Result := False r1i := r1.s_.count + 1 end r1i := r1i + 1 r2i := r2i + 1 end end end feature -- operations plus alias "+" (other: VALUE): VALUE -- adds current to other and returns a new object -- Was declared in VALUE as synonym of add. local res, empty: STRING_8 carry, r1i, r2i, d1, d2, d: INTEGER_32 ch: CHARACTER_8 r1, r2: VALUE do r1 := Current.clone_me r2 := other.clone_me if r1.s_.item (1) = '-' and r2.s_.item (1) /= '-' then create Result r1.negate Result := (r2 - r1).clone_me elseif r1.s_.item (1) /= '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 - r2).clone_me elseif r1.s_.item (1) = '-' and r2.s_.item (1) = '-' then create Result r1.negate r2.negate Result := (r1 + r2).clone_me Result.negate else r1.aligndecimal (r2) r1.alignwhole (r2) empty := "" res := empty.twin from r1i := r1.s_.count r2i := r2.s_.count until r1i < 1 or r2i < 1 loop ch := '0' if r1.s_.item (r1i) /= '.' and r2.s_.item (r2i) /= '.' then d1 := todigit (r1.s_.item (r1i)) d2 := todigit (r2.s_.item (r2i)) d := carry + d1 + d2 if d >= 10 then carry := d // 10 d := d - 10 else carry := 0 end ch := tochar (d) elseif r1.s_.item (r1i) = '.' and r2.s_.item (r2i) = '.' then ch := '.' end res.insert_character (ch, 1) r1i := r1i - 1 r2i := r2i - 1 end if carry > 0 then res.insert_character (tochar (carry), 1) end create Result.make_from_string (res) end end add (other: VALUE): VALUE -- adds current to other and returns a new object -- Was declared in VALUE as synonym of plus. local res, empty: STRING_8 carry, r1i, r2i, d1, d2, d: INTEGER_32 ch: CHARACTER_8 r1, r2: VALUE do r1 := Current.clone_me r2 := other.clone_me if r1.s_.item (1) = '-' and r2.s_.item (1) /= '-' then create Result r1.negate Result := (r2 - r1).clone_me elseif r1.s_.item (1) /= '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 - r2).clone_me elseif r1.s_.item (1) = '-' and r2.s_.item (1) = '-' then create Result r1.negate r2.negate Result := (r1 + r2).clone_me Result.negate else r1.aligndecimal (r2) r1.alignwhole (r2) empty := "" res := empty.twin from r1i := r1.s_.count r2i := r2.s_.count until r1i < 1 or r2i < 1 loop ch := '0' if r1.s_.item (r1i) /= '.' and r2.s_.item (r2i) /= '.' then d1 := todigit (r1.s_.item (r1i)) d2 := todigit (r2.s_.item (r2i)) d := carry + d1 + d2 if d >= 10 then carry := d // 10 d := d - 10 else carry := 0 end ch := tochar (d) elseif r1.s_.item (r1i) = '.' and r2.s_.item (r2i) = '.' then ch := '.' end res.insert_character (ch, 1) r1i := r1i - 1 r2i := r2i - 1 end if carry > 0 then res.insert_character (tochar (carry), 1) end create Result.make_from_string (res) end end minus alias "-" (other: VALUE): VALUE -- subtracts other from current and returns a new object -- Was declared in VALUE as synonym of subtract. local res, empty: STRING_8 r1, r2: VALUE r1i, r2i, d1, d2, dp, i, j: INTEGER_32 ch: CHARACTER_8 do r1 := Current.clone_me r2 := other.clone_me if r1.s_.item (1) = '-' and r2.s_.item (1) /= '-' then create Result r1.negate Result := (r1 + r2).clone_me Result.negate elseif r1.s_.item (1) /= '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 + r2).clone_me elseif r1.s_.item (1) = '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 + r2).clone_me else if r1 < r2 then Result := (r2 - r1).clone_me Result.negate else r1.aligndecimal (r2) r1.alignwhole (r2) empty := "" res := empty.twin from r1i := r1.s_.count r2i := r2.s_.count until r1i < 1 or r2i < 1 loop ch := '0' if r1.s_.item (r1i) /= '.' and r2.s_.item (r2i) /= '.' then d1 := todigit (r1.s_.item (r1i)) d2 := todigit (r2.s_.item (r2i)) if d1 < d2 then from i := r1i - 1 until i < 1 loop if r1.s_.item (i) /= '.' then dp := todigit (r1.s_.item (i)) if dp > 0 then r1.s_.put (tochar (dp - 1), i) from j := i + 1 until j > r1i - 1 loop if r1.s_.item (j) /= '.' then r1.s_.put (tochar (todigit (r1.s_.item (j)) + 9), j) end j := j + 1 end d1 := d1 + 10 i := 0 end end i := i - 1 end end ch := tochar (d1 - d2) elseif r1.s_.item (r1i) = '.' and r2.s_.item (r2i) = '.' then ch := '.' end res.insert_character (ch, 1) r1i := r1i - 1 r2i := r2i - 1 end create Result.make_from_string (res) end end end subtract (other: VALUE): VALUE -- subtracts other from current and returns a new object -- Was declared in VALUE as synonym of minus. local res, empty: STRING_8 r1, r2: VALUE r1i, r2i, d1, d2, dp, i, j: INTEGER_32 ch: CHARACTER_8 do r1 := Current.clone_me r2 := other.clone_me if r1.s_.item (1) = '-' and r2.s_.item (1) /= '-' then create Result r1.negate Result := (r1 + r2).clone_me Result.negate elseif r1.s_.item (1) /= '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 + r2).clone_me elseif r1.s_.item (1) = '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 + r2).clone_me else if r1 < r2 then Result := (r2 - r1).clone_me Result.negate else r1.aligndecimal (r2) r1.alignwhole (r2) empty := "" res := empty.twin from r1i := r1.s_.count r2i := r2.s_.count until r1i < 1 or r2i < 1 loop ch := '0' if r1.s_.item (r1i) /= '.' and r2.s_.item (r2i) /= '.' then d1 := todigit (r1.s_.item (r1i)) d2 := todigit (r2.s_.item (r2i)) if d1 < d2 then from i := r1i - 1 until i < 1 loop if r1.s_.item (i) /= '.' then dp := todigit (r1.s_.item (i)) if dp > 0 then r1.s_.put (tochar (dp - 1), i) from j := i + 1 until j > r1i - 1 loop if r1.s_.item (j) /= '.' then r1.s_.put (tochar (todigit (r1.s_.item (j)) + 9), j) end j := j + 1 end d1 := d1 + 10 i := 0 end end i := i - 1 end end ch := tochar (d1 - d2) elseif r1.s_.item (r1i) = '.' and r2.s_.item (r2i) = '.' then ch := '.' end res.insert_character (ch, 1) r1i := r1i - 1 r2i := r2i - 1 end create Result.make_from_string (res) end end end product alias "*" (other: VALUE): VALUE -- multiplies current by other and returns a new object -- Was declared in VALUE as synonym of multiply. local r1, r2, tr1, tr2, tr: VALUE r1dotpos, r2dotpos, precision, r1i, r2i, carry, d1, d2, d, diff, i: INTEGER_32 res, cres, tmp, empty: STRING_8 do r1 := Current.clone_me r2 := other.clone_me if r1.s_.item (1) = '-' and r2.s_.item (1) /= '-' then create Result r1.negate Result := (r1 * r2).clone_me Result.negate elseif r1.s_.item (1) /= '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 * r2).clone_me Result.negate elseif r1.s_.item (1) = '-' and r2.s_.item (1) = '-' then create Result r1.negate r2.negate Result := (r1 * r2).clone_me else r1dotpos := r1.s_.index_of ('.', 1) r2dotpos := r2.s_.index_of ('.', 1) precision := 0 if r1dotpos > 0 then precision := precision + r1.s_.count - r1dotpos r1.s_.remove (r1dotpos) end if r2dotpos > 0 then precision := precision + r2.s_.count - r2dotpos r2.s_.remove (r2dotpos) end empty := "" res := empty.twin from r2i := r2.s_.count until r2i < 1 loop cres := empty.twin carry := 0 from r1i := r1.s_.count until r1i < 1 loop d1 := todigit (r1.s_.item (r1i)) d2 := todigit (r2.s_.item (r2i)) d := carry + d1 * d2 if d >= 10 then carry := d // 10 d := d \\ 10 else carry := 0 end cres.insert_character (tochar (d), 1) r1i := r1i - 1 end if carry > 0 then cres.insert_character (tochar (carry), 1) end if res.count = 0 then res := cres.twin else create tmp.make_filled ('0', r2.s_.count - r2i) cres.append (tmp) create tr1.make_from_string (cres) create tr2.make_from_string (res) tr := tr1 + tr2 res := tr.s_.twin end r2i := r2i - 1 end if precision > 0 then if precision > res.count then diff := precision - res.count from i := 1 until i > diff loop res.insert_character ('0', 1) i := i + 1 end end res.insert_character ('.', res.count - precision + 1) end create Result.make_from_string (res) end end multiply (other: VALUE): VALUE -- multiplies current by other and returns a new object -- Was declared in VALUE as synonym of product. local r1, r2, tr1, tr2, tr: VALUE r1dotpos, r2dotpos, precision, r1i, r2i, carry, d1, d2, d, diff, i: INTEGER_32 res, cres, tmp, empty: STRING_8 do r1 := Current.clone_me r2 := other.clone_me if r1.s_.item (1) = '-' and r2.s_.item (1) /= '-' then create Result r1.negate Result := (r1 * r2).clone_me Result.negate elseif r1.s_.item (1) /= '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 * r2).clone_me Result.negate elseif r1.s_.item (1) = '-' and r2.s_.item (1) = '-' then create Result r1.negate r2.negate Result := (r1 * r2).clone_me else r1dotpos := r1.s_.index_of ('.', 1) r2dotpos := r2.s_.index_of ('.', 1) precision := 0 if r1dotpos > 0 then precision := precision + r1.s_.count - r1dotpos r1.s_.remove (r1dotpos) end if r2dotpos > 0 then precision := precision + r2.s_.count - r2dotpos r2.s_.remove (r2dotpos) end empty := "" res := empty.twin from r2i := r2.s_.count until r2i < 1 loop cres := empty.twin carry := 0 from r1i := r1.s_.count until r1i < 1 loop d1 := todigit (r1.s_.item (r1i)) d2 := todigit (r2.s_.item (r2i)) d := carry + d1 * d2 if d >= 10 then carry := d // 10 d := d \\ 10 else carry := 0 end cres.insert_character (tochar (d), 1) r1i := r1i - 1 end if carry > 0 then cres.insert_character (tochar (carry), 1) end if res.count = 0 then res := cres.twin else create tmp.make_filled ('0', r2.s_.count - r2i) cres.append (tmp) create tr1.make_from_string (cres) create tr2.make_from_string (res) tr := tr1 + tr2 res := tr.s_.twin end r2i := r2i - 1 end if precision > 0 then if precision > res.count then diff := precision - res.count from i := 1 until i > diff loop res.insert_character ('0', 1) i := i + 1 end end res.insert_character ('.', res.count - precision + 1) end create Result.make_from_string (res) end end quotient alias "/" (other: VALUE): VALUE -- divides current by other and returns a new object -- Was declared in VALUE as synonym of divide. local r1, r2, tr, diff, x, sum, prod: VALUE r1dotpos, r2dotpos, r1precision, r2precision, r1numtrailingzeros, r2numtrailingzeros, scalefactor, i, startpos, r1i, d: INTEGER_32 s, res, empty: STRING_8 pad: detachable STRING_8 stoploop: BOOLEAN ch: CHARACTER_8 do r1 := Current.clone_me r2 := other.clone_me if r1.is_equal (zero) then Result := zero elseif r1.s_.item (1) = '-' and r2.s_.item (1) /= '-' then create Result r1.negate Result := (r1 / r2).clone_me Result.negate elseif r1.s_.item (1) /= '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 / r2).clone_me Result.negate elseif r1.s_.item (1) = '-' and r2.s_.item (1) = '-' then create Result r1.negate r2.negate Result := (r1 / r2).clone_me else r1dotpos := r1.s_.index_of ('.', 1) r2dotpos := r2.s_.index_of ('.', 1) r1precision := 0 r2precision := 0 if r1dotpos > 0 then r1precision := r1.s_.count - r1dotpos r1.s_.remove (r1dotpos) r1.normalize end if r2dotpos > 0 then r2precision := r2.s_.count - r2dotpos r2.s_.remove (r2dotpos) r2.normalize end pad := Void if r1precision > r2precision then pad := r2.s_ elseif r2precision > r1precision then pad := r1.s_ end if pad /= Void then create s.make_filled ('0', (r1precision - r2precision).abs) pad.append (s) end r1numtrailingzeros := 0 r2numtrailingzeros := 0 scalefactor := 0 from i := r1.s_.count until i < 1 loop if r1.s_.item (i) = '0' then r1numtrailingzeros := r1numtrailingzeros + 1 else i := 0 end i := i - 1 end from i := r2.s_.count until i < 1 loop if r2.s_.item (i) = '0' then r2numtrailingzeros := r2numtrailingzeros + 1 else i := 0 end i := i - 1 end if r1numtrailingzeros < r2numtrailingzeros then create s.make_filled ('0', r2numtrailingzeros - r1numtrailingzeros) r1.s_.append (s) scalefactor := scalefactor + r2numtrailingzeros - r1numtrailingzeros r1numtrailingzeros := r2numtrailingzeros end if r1numtrailingzeros - r2numtrailingzeros < divisiondecimalprecision_ + 1 then scalefactor := scalefactor + divisiondecimalprecision_ + 1 - (r1numtrailingzeros - r2numtrailingzeros) create s.make_filled ('0', divisiondecimalprecision_ + 1 - (r1numtrailingzeros - r2numtrailingzeros)) r1.s_.append (s) end if r1 < r2 then scalefactor := scalefactor + r2.s_.count - r1.s_.count + 1 create s.make_filled ('0', r2.s_.count - r1.s_.count + 1) r1.s_.append (s) end if r1.s_.count - r2.s_.count < divisiondecimalprecision_ then scalefactor := scalefactor + divisiondecimalprecision_ - (r1.s_.count - r2.s_.count) create s.make_filled ('0', divisiondecimalprecision_ - (r1.s_.count - r2.s_.count)) r1.s_.append (s) end startpos := r2.s_.count + 1 if r1.s_.count > r2.s_.count then from stoploop := False until startpos > r1.s_.count or stoploop loop s := r1.s_.substring (1, startpos - 1) create tr.make_from_string (s) if tr >= r2 then stoploop := True else startpos := startpos + 1 end end end empty := "" res := empty.twin create diff from r1i := startpos - 1 until r1i > r1.s_.count loop if r1i = startpos - 1 then create x.make_from_string (r1.s_.substring (1, r1i)) else diff.s_.insert_character (r1.s_.item (r1i), diff.s_.count + 1) create x x := diff.clone_me end d := 0 create sum from stoploop := False until stoploop loop sum := sum + r2 if sum > x then stoploop := True else d := d + 1 end end ch := tochar (d) res.insert_character (ch, res.count + 1) create prod create s.make_filled (ch, 1) create tr.make_from_string (s) prod := tr * r2 diff := x - prod r1i := r1i + 1 end if scalefactor > 0 then if scalefactor > res.count then create s.make_filled ('0', scalefactor - res.count) res.insert_string (s, 1) end res.insert_character ('.', res.count - scalefactor + 1) end create Result.make_from_string (res) end end divide (other: VALUE): VALUE -- divides current by other and returns a new object -- Was declared in VALUE as synonym of quotient. local r1, r2, tr, diff, x, sum, prod: VALUE r1dotpos, r2dotpos, r1precision, r2precision, r1numtrailingzeros, r2numtrailingzeros, scalefactor, i, startpos, r1i, d: INTEGER_32 s, res, empty: STRING_8 pad: detachable STRING_8 stoploop: BOOLEAN ch: CHARACTER_8 do r1 := Current.clone_me r2 := other.clone_me if r1.is_equal (zero) then Result := zero elseif r1.s_.item (1) = '-' and r2.s_.item (1) /= '-' then create Result r1.negate Result := (r1 / r2).clone_me Result.negate elseif r1.s_.item (1) /= '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 / r2).clone_me Result.negate elseif r1.s_.item (1) = '-' and r2.s_.item (1) = '-' then create Result r1.negate r2.negate Result := (r1 / r2).clone_me else r1dotpos := r1.s_.index_of ('.', 1) r2dotpos := r2.s_.index_of ('.', 1) r1precision := 0 r2precision := 0 if r1dotpos > 0 then r1precision := r1.s_.count - r1dotpos r1.s_.remove (r1dotpos) r1.normalize end if r2dotpos > 0 then r2precision := r2.s_.count - r2dotpos r2.s_.remove (r2dotpos) r2.normalize end pad := Void if r1precision > r2precision then pad := r2.s_ elseif r2precision > r1precision then pad := r1.s_ end if pad /= Void then create s.make_filled ('0', (r1precision - r2precision).abs) pad.append (s) end r1numtrailingzeros := 0 r2numtrailingzeros := 0 scalefactor := 0 from i := r1.s_.count until i < 1 loop if r1.s_.item (i) = '0' then r1numtrailingzeros := r1numtrailingzeros + 1 else i := 0 end i := i - 1 end from i := r2.s_.count until i < 1 loop if r2.s_.item (i) = '0' then r2numtrailingzeros := r2numtrailingzeros + 1 else i := 0 end i := i - 1 end if r1numtrailingzeros < r2numtrailingzeros then create s.make_filled ('0', r2numtrailingzeros - r1numtrailingzeros) r1.s_.append (s) scalefactor := scalefactor + r2numtrailingzeros - r1numtrailingzeros r1numtrailingzeros := r2numtrailingzeros end if r1numtrailingzeros - r2numtrailingzeros < divisiondecimalprecision_ + 1 then scalefactor := scalefactor + divisiondecimalprecision_ + 1 - (r1numtrailingzeros - r2numtrailingzeros) create s.make_filled ('0', divisiondecimalprecision_ + 1 - (r1numtrailingzeros - r2numtrailingzeros)) r1.s_.append (s) end if r1 < r2 then scalefactor := scalefactor + r2.s_.count - r1.s_.count + 1 create s.make_filled ('0', r2.s_.count - r1.s_.count + 1) r1.s_.append (s) end if r1.s_.count - r2.s_.count < divisiondecimalprecision_ then scalefactor := scalefactor + divisiondecimalprecision_ - (r1.s_.count - r2.s_.count) create s.make_filled ('0', divisiondecimalprecision_ - (r1.s_.count - r2.s_.count)) r1.s_.append (s) end startpos := r2.s_.count + 1 if r1.s_.count > r2.s_.count then from stoploop := False until startpos > r1.s_.count or stoploop loop s := r1.s_.substring (1, startpos - 1) create tr.make_from_string (s) if tr >= r2 then stoploop := True else startpos := startpos + 1 end end end empty := "" res := empty.twin create diff from r1i := startpos - 1 until r1i > r1.s_.count loop if r1i = startpos - 1 then create x.make_from_string (r1.s_.substring (1, r1i)) else diff.s_.insert_character (r1.s_.item (r1i), diff.s_.count + 1) create x x := diff.clone_me end d := 0 create sum from stoploop := False until stoploop loop sum := sum + r2 if sum > x then stoploop := True else d := d + 1 end end ch := tochar (d) res.insert_character (ch, res.count + 1) create prod create s.make_filled (ch, 1) create tr.make_from_string (s) prod := tr * r2 diff := x - prod r1i := r1i + 1 end if scalefactor > 0 then if scalefactor > res.count then create s.make_filled ('0', scalefactor - res.count) res.insert_string (s, 1) end res.insert_character ('.', res.count - scalefactor + 1) end create Result.make_from_string (res) end end opposite alias "-": VALUE -- unary minus do create Result.make_from_string (s_) Result.negate end identity alias "+": VALUE -- unary plus do create Result.make_from_string (s_) end zero: VALUE -- neutral element for "+" and "-" do create Result.make_from_string ("0") end one: VALUE -- neutral element for "*" and "/" do create Result.make_from_string ("1") end divisible (other: VALUE): BOOLEAN -- may current object be divided by other? do Result := not other.is_equal (zero) end exponentiable (other: NUMERIC): BOOLEAN -- may current object be elevated to the power other? do Result := False end is_natural: BOOLEAN do Result := is_integer and Current >= zero end is_natural1: BOOLEAN do Result := is_integer and Current >= one end is_integer: BOOLEAN do Result := s_.index_of ('.', 1) = 0 end set_division_precision (i: INTEGER_32) -- sets divisionDecimalPrecision_ to 'i' require greater_than_zero: i > 0 do divisiondecimalprecision_ := i end feature -- Conversion as_double: REAL_64 -- represent as a DOUBLE do Result := precise_out.to_double end feature -- printing precise_out: STRING_8 -- what is the string representation of VALUE_IMP do Result := s_.twin end out: STRING_8 -- return a representation of the number rounded to two decimal places local myclone: VALUE precision, missingzeros, idxdot, rounddigits: INTEGER_32 pad: STRING_8 do myclone := Current.clone_me rounddigits := 2 myclone.round (rounddigits) idxdot := myclone.s_.index_of ('.', 1) if idxdot = 0 then myclone.s_.append_character ('.') create pad.make_filled ('0', rounddigits) myclone.s_.append_string (pad) else precision := myclone.s_.count - idxdot missingzeros := rounddigits - precision if missingzeros > 0 then create pad.make_filled ('0', missingzeros) myclone.s_.append_string (pad) end end Result := myclone.s_.twin end feature -- rounding round_to (digits: INTEGER_32): VALUE -- rounds the current VALUE_IMP to 'digits' -- symmetric rounding as in Excel http://support.microsoft.com/kb/196652 -- -2.6 is rounded to -3 require digits >= 0 do Result := Current.clone_me Result.round (digits) end precise_out_to (digits: INTEGER_32): STRING_8 -- returns the precise string representation to 'digits' require digits >= 0 local dotpos: INTEGER_32 do Result := precise_out dotpos := Result.index_of ('.', 1) if dotpos > 0 then Result := Result.substring (1, dotpos + digits) end end round (digits: INTEGER_32) -- rounds this instance to the specified number of digits require digits >= 0 local dotpos: INTEGER_32 neg: BOOLEAN s, tmp: STRING_8 x, y: VALUE do dotpos := s_.index_of ('.', 1) if dotpos > 0 then neg := False if s_.item (1) = '-' then negate neg := True end create s.make_filled ('0', digits) s.append ("5") s.insert_string ("0.", 1) create x.make_from_string (s) y := Current + x dotpos := y.s_.index_of ('.', 1) if dotpos /= 0 then tmp := y.s_.substring (1, dotpos + digits) else tmp := y.precise_out end s_ := tmp.twin if neg then negate end end normalize end negate -- negates the number local tmp: STRING_8 do if s_.item (1) = '-' then tmp := s_.substring (2, s_.count) s_ := tmp.twin else s_.insert_character ('-', 1) end end Epsilon_singleton: VALUE_SINGLETON once create Result end set_epsilon (v: STRING_8) do Epsilon_singleton.set_epsilon (v) end is_imprecise_equal alias "|~" (other: VALUE): BOOLEAN do if Epsilon_singleton.epsilon = create {VALUE}.make_from_string ("0.0") then Result := Current = other else Result := (Current - other).absolute <= Epsilon_singleton.epsilon end end is_imprecise_equal_negation alias "|/~" (other: VALUE): BOOLEAN do if Epsilon_singleton.epsilon = create {VALUE}.make_from_string ("0.0") then Result := Current /= other else Result := not (Current |~ other) end end is_imprecise_less alias "|<" (other: VALUE): BOOLEAN do if Epsilon_singleton.epsilon = create {VALUE}.make_from_string ("0.0") then Result := Current < other else Result := Current < (other - Epsilon_singleton.epsilon) end end is_imprecise_less_equal alias "|<~" (other: VALUE): BOOLEAN do if Epsilon_singleton.epsilon = create {VALUE}.make_from_string ("0.0") then Result := Current <= other else Result := (Current |~ other) or (Current < (other - Epsilon_singleton.epsilon)) end end is_imprecise_greater alias "|>" (other: VALUE): BOOLEAN do if Epsilon_singleton.epsilon = create {VALUE}.make_from_string ("0.0") then Result := Current > other else Result := Current > (other + Epsilon_singleton.epsilon) end end is_imprecise_greater_equal alias "|>~" (other: VALUE): BOOLEAN do if Epsilon_singleton.epsilon = create {VALUE}.make_from_string ("0.0") then Result := Current >= other else Result := (Current |~ other) or (Current > (other + Epsilon_singleton.epsilon)) end end absolute: VALUE do if Current >= Current.zero then Result := Current.clone_me else Result := opposite.clone_me end ensure Result >= Result.zero end feature {VALUE} -- private methods ensurevalid (s: STRING_8): BOOLEAN -- check if the given string is of the correct format require non_void: s /= Void non_empty: not s.is_empty local seendot: BOOLEAN i: INTEGER_32 do seendot := False Result := True from i := 1 until i > s.count loop if s.item (i) = '-' then if i > 1 then Result := False i := s.count + 1 end elseif s.item (i) = '.' then if i = s.count or seendot then Result := False i := s.count + 1 end seendot := True elseif not s.item (i).is_digit then Result := False i := s.count + 1 end i := i + 1 end end normalize -- standardizes the format of the real number require non_empty: not s_.is_empty local i, inspos, dotpos, firstnonzeropos, lastnonzeropos: INTEGER_32 seenminus, prependzero, hasdot: BOOLEAN whole, fraction, empty, tmp, zerostr: STRING_8 do empty := "" zerostr := "0" seenminus := False prependzero := False from i := 1 until i > s_.count loop if s_.item (i) = '-' then seenminus := True elseif s_.item (i) = '.' then if i = 1 or seenminus then prependzero := True end end i := i + 1 end if prependzero then inspos := 1 if seenminus then inspos := 2 end s_.insert_character ('0', inspos) end if (seenminus) then negate end dotpos := s_.index_of ('.', 1) if dotpos = 0 then hasdot := False whole := s_.twin fraction := empty.twin else hasdot := True tmp := s_.substring (1, dotpos - 1) whole := tmp.twin tmp := s_.substring (dotpos + 1, s_.count) fraction := tmp.twin end if not whole.is_empty then firstnonzeropos := whole.count + 1 from i := 1 until i > whole.count loop if whole.item (i) /= '0' then firstnonzeropos := i i := whole.count + 1 end i := i + 1 end if firstnonzeropos = whole.count + 1 then whole := zerostr.twin else tmp := whole.substring (firstnonzeropos, whole.count) whole := tmp.twin end end if not fraction.is_empty then lastnonzeropos := -1 from i := fraction.count until i < 1 loop if fraction.item (i) /= '0' then lastnonzeropos := i i := 0 end i := i - 1 end if lastnonzeropos = -1 then fraction := empty.twin else tmp := fraction.substring (1, lastnonzeropos) fraction := tmp.twin end end s_ := whole.twin if hasdot and not fraction.is_empty then s_.append (".") s_.append (fraction) end if seenminus and not s_.is_equal ("0") then s_.insert_character ('-', 1) end end todigit (ch: CHARACTER_8): INTEGER_32 -- converts a character to a digit local chzero: CHARACTER_8 do chzero := '0' Result := ch.code - chzero.code end tochar (d: INTEGER_32): CHARACTER_8 -- converts a digit to a character local chzero: CHARACTER_8 do chzero := '0' Result := (chzero.code + d).to_character_8 end aligndecimal (other: VALUE) -- used to align the fractional parts of the given parameters local mydotpos, otherdotpos, myprec, otherprec, numtopad, i: INTEGER_32 pad: detachable STRING_8 do mydotpos := s_.index_of ('.', 1) otherdotpos := other.s_.index_of ('.', 1) if mydotpos /= 0 and otherdotpos /= 0 then myprec := s_.count - mydotpos otherprec := other.s_.count - otherdotpos if myprec /= otherprec then pad := Void if myprec < otherprec then pad := s_ else pad := other.s_ end numtopad := (myprec - otherprec).abs from i := 1 until i > numtopad loop pad.append_character ('0') i := i + 1 end end elseif mydotpos /= 0 or otherdotpos /= 0 then if mydotpos /= 0 then myprec := s_.count - mydotpos else myprec := 0 end if otherdotpos /= 0 then otherprec := other.s_.count - otherdotpos else otherprec := 0 end pad := Void if myprec < otherprec then pad := s_ else pad := other.s_ end numtopad := (myprec - otherprec).abs pad.append_character ('.') from i := 1 until i > numtopad loop pad.append_character ('0') i := i + 1 end end end alignwhole (other: VALUE) -- used to align the integer parts of the given parameters local mydotpos, otherdotpos, mywholelength, otherwholelength, menegoffset, othernegoffset, inspos, numtopad, i: INTEGER_32 meneg, otherneg: BOOLEAN pad: detachable STRING_8 do mydotpos := s_.index_of ('.', 1) otherdotpos := other.s_.index_of ('.', 1) meneg := s_.item (1) = '-' otherneg := other.s_.item (1) = '-' if mydotpos /= 0 then if meneg then menegoffset := 1 else menegoffset := 0 end mywholelength := mydotpos - menegoffset - 1 else mywholelength := s_.count end if otherdotpos /= 0 then if otherneg then othernegoffset := 1 else othernegoffset := 0 end otherwholelength := otherdotpos - othernegoffset - 1 else otherwholelength := other.s_.count end if mywholelength /= otherwholelength then pad := Void if mywholelength < otherwholelength then pad := s_ else pad := other.s_ end inspos := 1 if pad.item (1) = '-' then inspos := inspos + 1 end numtopad := (mywholelength - otherwholelength).abs from i := 1 until i > numtopad loop pad.insert_character ('0', inspos) i := i + 1 end end end invariant divisiondecimalprecision_ > 0 end -- class VALUE
Generated by ISE EiffelStudio