note description: "[ An arbitrary precision library for decimal numbers. Following the 'General Decimal Arithmetic Specification' of gobo MA_DECIMAL. Creation make_from_string make_from_decimal make_from_int make_from_big_int make_from_rational Queries as_big_int: BIG_INTEGER as_rational: RATIONAL as_real32: REAL_32 as_real64: REAL_64 exp alias "^" (power: DECIMAL): DECIMAL identity alias "+": DECIMAL is_equal (other: DECIMAL): BOOLEAN is_greater alias ">" (other: DECIMAL): BOOLEAN is_greater_equal alias ">=" (other: DECIMAL): BOOLEAN is_integer: BOOLEAN is_less alias "<" (other: DECIMAL): BOOLEAN is_less_equal alias "<=" (other: DECIMAL): BOOLEAN is_natural: BOOLEAN is_natural1: BOOLEAN is_negative: BOOLEAN is_real32: BOOLEAN is_real64: BOOLEAN max (other: DECIMAL): DECIMAL min (other: DECIMAL): DECIMAL minus alias "-" (other: DECIMAL): DECIMAL one: DECIMAL opposite alias "-": DECIMAL out: STRING plus alias "+" (other: DECIMAL): DECIMAL precise_out: STRING precise_out_to (digits: INTEGER): STRING product alias "*" (other: DECIMAL): DECIMAL quotient alias "/" (other: DECIMAL): DECIMAL sqrt: DECIMAL round_to (digits: INTEGER): DECIMAL zero: DECIMAL Commands round (digits: INTEGER) set_precision (precision: INTEGER) ]" author: "JSO, DC, and CD" date: "$Date$" revision: "$Revision$" class DECIMAL inherit COMPARABLE redefine is_equal, default_create, out end DEBUG_OUTPUT redefine is_equal, default_create, out end create make_from_string, make_from_decimal, make_from_int, default_create, make_from_string_ctx, make_with_precision, make_from_big_int, make_from_rational convert make_from_string ({STRING_8}), make_from_big_int ({BIG_INTEGER}), make_from_rational ({RATIONAL}), as_real64: {REAL_64}, as_big_int: {BIG_INTEGER}, as_rational: {RATIONAL}, out: {STRING_8} feature {NONE} -- constructors make_with_precision (a_value: STRING_8; a_precision: INTEGER_32) -- Make a new decimal from a_value with `a_precision require positive_precision: a_precision > 0 local l_ma_decimal_ctx: MA_DECIMAL_CONTEXT do create l_ma_decimal_ctx.make (a_precision, 4) create ma_decimal.make_from_string_ctx (a_value, l_ma_decimal_ctx) ma_decimal_ctx := l_ma_decimal_ctx item := decimal_2_value_string end default_create -- create an empty object (it's equivalent to 0) local empty: STRING_8 do empty := "0" item := empty.twin ma_decimal := decimal_setup (item, Default_precision + 1) ensure then item.is_equal ("0") end make_from_string (s: STRING_8) -- create an object from string 's' require non_void: s /= Void non_empty: not s.is_empty has_correct_format: ensurevalid (s) local precision: INTEGER_32 value: LIST [STRING_8] do value := s.split (Exponent_char.to_character_32) precision := value [1].count if precision < (Default_precision + value [1].split (Decimal_char.to_character_32) [1].count) then precision := Default_precision + value [1].split (Decimal_char.to_character_32) [1].count end ma_decimal := decimal_setup (s.twin, precision) item := decimal_2_value_string end make_from_int (int: INTEGER_32) -- create a DECIMAL object from integer m do item := int.out ma_decimal_ctx := decimal_ctx_for_value ma_decimal_ctx.set_digits (item.count + Default_precision) create ma_decimal.make_from_integer (int) end make_from_big_int (big_int: BIG_INTEGER) -- Create from BIG_INTEGER do make_from_string (big_int.out) end make_from_rational (rational: RATIONAL) -- Create from RATIONAL do make_from_string (rational.out) end feature -- comparison is_equal (other: DECIMAL): BOOLEAN -- check whether other is equal to current or not do Result := ma_decimal.is_equal (other.ma_decimal) end is_less alias "<" (other: DECIMAL): BOOLEAN -- whether current is less than other do Result := ma_decimal < other.ma_decimal end feature -- operations plus alias "+" (other: DECIMAL): DECIMAL -- adds current to other and returns a new object -- The new DECIMAL will have a precision based on the criteria in calculate_new_precision -- Was declared in DECIMAL as synonym of add. local l_decimal: MA_DECIMAL new_context: MA_DECIMAL_CONTEXT digits: INTEGER_32 do digits := calculate_new_precision (other) create new_context.make (digits, Default_rounding) create l_decimal.make_zero l_decimal.set_shared_decimal_context (new_context) l_decimal := Current.ma_decimal.add (other.ma_decimal, l_decimal.shared_decimal_context) create Result.make_from_decimal (l_decimal) end add (other: DECIMAL): DECIMAL -- adds current to other and returns a new object -- The new DECIMAL will have a precision based on the criteria in calculate_new_precision -- Was declared in DECIMAL as synonym of plus. local l_decimal: MA_DECIMAL new_context: MA_DECIMAL_CONTEXT digits: INTEGER_32 do digits := calculate_new_precision (other) create new_context.make (digits, Default_rounding) create l_decimal.make_zero l_decimal.set_shared_decimal_context (new_context) l_decimal := Current.ma_decimal.add (other.ma_decimal, l_decimal.shared_decimal_context) create Result.make_from_decimal (l_decimal) end minus alias "-" (other: DECIMAL): DECIMAL -- subtracts other from current and returns a new object -- The new DECIMAL will have a precision based on the criteria in calculate_new_precision -- Was declared in DECIMAL as synonym of subtract. local l_decimal: MA_DECIMAL new_context: MA_DECIMAL_CONTEXT digits: INTEGER_32 do digits := calculate_new_precision (other) create new_context.make (digits, Default_rounding) create l_decimal.make_zero l_decimal.set_shared_decimal_context (new_context) l_decimal := Current.ma_decimal.subtract (other.ma_decimal, l_decimal.shared_decimal_context) create Result.make_from_decimal (l_decimal) end subtract (other: DECIMAL): DECIMAL -- subtracts other from current and returns a new object -- The new DECIMAL will have a precision based on the criteria in calculate_new_precision -- Was declared in DECIMAL as synonym of minus. local l_decimal: MA_DECIMAL new_context: MA_DECIMAL_CONTEXT digits: INTEGER_32 do digits := calculate_new_precision (other) create new_context.make (digits, Default_rounding) create l_decimal.make_zero l_decimal.set_shared_decimal_context (new_context) l_decimal := Current.ma_decimal.subtract (other.ma_decimal, l_decimal.shared_decimal_context) create Result.make_from_decimal (l_decimal) end product alias "*" (other: DECIMAL): DECIMAL -- multiplies current by other and returns a new object -- The new DECIMAL will have a precision based on the criteria in calculate_new_precision -- Was declared in DECIMAL as synonym of multiply. local l_decimal: MA_DECIMAL new_context: MA_DECIMAL_CONTEXT digits: INTEGER_32 do digits := calculate_new_precision (other) create new_context.make (digits, Default_rounding) create l_decimal.make_zero l_decimal.set_shared_decimal_context (new_context) l_decimal := Current.ma_decimal.multiply (other.ma_decimal, l_decimal.shared_decimal_context) create Result.make_from_decimal (l_decimal) end multiply (other: DECIMAL): DECIMAL -- multiplies current by other and returns a new object -- The new DECIMAL will have a precision based on the criteria in calculate_new_precision -- Was declared in DECIMAL as synonym of product. local l_decimal: MA_DECIMAL new_context: MA_DECIMAL_CONTEXT digits: INTEGER_32 do digits := calculate_new_precision (other) create new_context.make (digits, Default_rounding) create l_decimal.make_zero l_decimal.set_shared_decimal_context (new_context) l_decimal := Current.ma_decimal.multiply (other.ma_decimal, l_decimal.shared_decimal_context) create Result.make_from_decimal (l_decimal) end quotient alias "/" (other: DECIMAL): DECIMAL -- divides current by other and returns a new object -- The new DECIMAL will have a precision based on the criteria in calculate_new_precision -- Was declared in DECIMAL as synonym of divide. require other_non_zero: other /~ zero local l_decimal: MA_DECIMAL new_context: MA_DECIMAL_CONTEXT digits: INTEGER_32 do digits := calculate_new_precision (other) create new_context.make (digits, Default_rounding) create l_decimal.make_zero l_decimal.set_shared_decimal_context (new_context) l_decimal := Current.ma_decimal.divide (other.ma_decimal, l_decimal.shared_decimal_context) create Result.make_from_decimal (l_decimal) end divide (other: DECIMAL): DECIMAL -- divides current by other and returns a new object -- The new DECIMAL will have a precision based on the criteria in calculate_new_precision -- Was declared in DECIMAL as synonym of quotient. require other_non_zero: other /~ zero local l_decimal: MA_DECIMAL new_context: MA_DECIMAL_CONTEXT digits: INTEGER_32 do digits := calculate_new_precision (other) create new_context.make (digits, Default_rounding) create l_decimal.make_zero l_decimal.set_shared_decimal_context (new_context) l_decimal := Current.ma_decimal.divide (other.ma_decimal, l_decimal.shared_decimal_context) create Result.make_from_decimal (l_decimal) end sqrt: DECIMAL -- calculates square root for this instance require non_negative: not is_negative local l_epsilon: DECIMAL l_root: DECIMAL do l_epsilon := create {attached DECIMAL}.make_from_string (Epsilon) if out ~ zero.out then Result := zero else from l_root := clone_me until ((l_root - (Current / l_root)).absolute <= create {DECIMAL}.make_from_string (Epsilon) * l_root) loop l_root := ((Current / l_root) + l_root) / create {DECIMAL}.make_from_string ("2.0") end Result := l_root end end exp alias "^" (p: DECIMAL): DECIMAL -- calculates the value of current instance raised to 'power' (capped to 500 digits of precision) -- Was declared in DECIMAL as synonym of exponent and power. require is_exponentiable: exponentiable (p) do if p.is_integer then Result := integer_exp (Current, p.ma_decimal.to_integer.abs) else Result := real_exp (Current, p.absolute) end if p.is_negative then Result := one / Result end end exponent (p: DECIMAL): DECIMAL -- calculates the value of current instance raised to 'power' (capped to 500 digits of precision) -- Was declared in DECIMAL as synonym of exp and power. require is_exponentiable: exponentiable (p) do if p.is_integer then Result := integer_exp (Current, p.ma_decimal.to_integer.abs) else Result := real_exp (Current, p.absolute) end if p.is_negative then Result := one / Result end end power (p: DECIMAL): DECIMAL -- calculates the value of current instance raised to 'power' (capped to 500 digits of precision) -- Was declared in DECIMAL as synonym of exp and exponent. require is_exponentiable: exponentiable (p) do if p.is_integer then Result := integer_exp (Current, p.ma_decimal.to_integer.abs) else Result := real_exp (Current, p.absolute) end if p.is_negative then Result := one / Result end end negate -- negates Current local l_decimal: MA_DECIMAL do l_decimal := ma_decimal.opposite Current.make_from_decimal (l_decimal) end absolute: DECIMAL -- returns the absoulte value of Current do create Result.make_from_decimal (ma_decimal.abs) end floor: DECIMAL -- Returns the greatest integer less than or equal to Current local curr_abs: DECIMAL floored_absolute: DECIMAL do curr_abs := Current.absolute create floored_absolute.make_from_string (curr_abs.value_precise_out.split (Decimal_char.to_character_32) [1]) if is_integer then Result := Current.twin else if is_negative then floored_absolute.negate Result := floored_absolute - create {DECIMAL}.make_from_string ("1") else Result := floored_absolute end end end ceiling: DECIMAL -- Returns the smallest integer greater than or equal to Current local curr_abs: DECIMAL floored_absolute: DECIMAL do curr_abs := Current.absolute create floored_absolute.make_from_string (curr_abs.value_precise_out.split (Decimal_char.to_character_32) [1]) if is_integer then Result := Current.twin else if is_negative then floored_absolute.negate Result := floored_absolute else Result := floored_absolute + create {DECIMAL}.make_from_string ("1") end end end feature -- conversion as_real32: REAL_32 -- represent as a REAL_32 require valid_real32: is_real32 do Result := item.to_real_32 end as_real64: REAL_64 -- represent as a REAL_64 require valid_real64: is_real64 do Result := ma_decimal.to_double end as_big_int: BIG_INTEGER -- Represent as BIG_INTEGER require is_integer: is_integer do create Result.make_from_string (item) end as_rational: RATIONAL -- Represent as RATIONAL do create Result.make_from_string (item) end feature -- queries opposite alias "-": DECIMAL -- unary minus do create Result.make_from_decimal (ma_decimal.opposite) end identity alias "+": DECIMAL -- unary plus do create Result.make_from_decimal (ma_decimal) end is_natural: BOOLEAN -- Is Current an Natural number? do Result := is_integer and Current >= zero end is_natural1: BOOLEAN -- Is Current an Natural1 number? do Result := is_integer and Current >= one end is_integer: BOOLEAN -- Is Current an integer number? do Result := ma_decimal.is_integer end is_real32: BOOLEAN local correct_length, in_range: BOOLEAN upper, lower: DECIMAL do create upper.make_from_string ("-3.4") create lower.make_from_string ("3.4") upper := upper * (create {DECIMAL}.make_from_string ("10")).exp (create {DECIMAL}.make_from_string ("38")) lower := lower * (create {DECIMAL}.make_from_string ("10")).exp (create {DECIMAL}.make_from_string ("38")) correct_length := item.count <= 7 in_range := Current > create {DECIMAL}.make_from_string ("-3.4") end is_real64: BOOLEAN -- Is Current a double? do Result := ma_decimal.is_double end is_negative: BOOLEAN -- Is Current less than or equal to zero? do Result := ma_decimal.is_negative and Current /~ zero end debug_output: STRING_8 -- String that should be displayed in debugger to represent Current. do Result := out end out: STRING_8 -- return a representation of the number rounded to two decimal places do Result := value_out end precise_out: STRING_8 -- return precise representation of the number do Result := value_precise_out end zero: DECIMAL -- neutral element for "+" and "-" do create Result.make_from_string ("0") end one: DECIMAL -- neutral element for "*" and "/" do create Result.make_from_string ("1") end divisible (other: DECIMAL): BOOLEAN -- may current object be divided by other? do Result := not other.is_equal (zero) end exponentiable (other: DECIMAL): BOOLEAN -- may current object be elevated to the power other? do Result := True end feature -- rounding round_to (digits: INTEGER_32): DECIMAL -- Returns a decimal that is the current value rounded to the specified 'digits' number of decimal places -- Uses default strategy as set in ma_decimal_ctx (half up) require positive_digits: digits >= 0 local l_decimal_ctx: MA_DECIMAL_CONTEXT decimal_left: INTEGER_32 decimal_left_int: BIG_INTEGER do decimal_left := value_precise_out.split (Decimal_char.to_character_32) [1].count if is_negative then decimal_left := decimal_left - 1 end decimal_left_int := create {attached BIG_INTEGER}.make_from_string (value_precise_out.split (Decimal_char.to_character_32) [1]) if decimal_left_int ~ create {BIG_INTEGER}.make_from_string ("0") then decimal_left := 0 end if digits + decimal_left <= 0 then decimal_left := 1 end create l_decimal_ctx.make (decimal_left + digits, ma_decimal_ctx.rounding_mode) create Result.make_from_decimal (create {MA_DECIMAL}.make_from_string_ctx (item, l_decimal_ctx)) end round (digits: INTEGER_32) -- rounds this instance to the specified digits number of decimal places require positive_digits: digits >= 0 local l_decimal_ctx: MA_DECIMAL_CONTEXT decimal_left: INTEGER_32 do decimal_left := value_precise_out.split (Decimal_char.to_character_32) [1].count if is_negative then decimal_left := decimal_left - 1 end if (value_precise_out.split (Decimal_char.to_character_32) [1] [1]).out ~ "0" or (is_negative and then (value_precise_out.split (Decimal_char.to_character_32) [1] [2]).out ~ "0") then decimal_left := decimal_left - 1 end create l_decimal_ctx.make (decimal_left + digits, ma_decimal_ctx.rounding_mode) Current.make_from_decimal (create {MA_DECIMAL}.make_from_string_ctx (item, l_decimal_ctx)) end precise_out_to (digits: INTEGER_32): STRING_8 -- returns the precise string representation to rounded to the specified 'digits' number of decimal places require positive_digits: digits >= 0 local digit_list: LIST [STRING_8] decimal_left: STRING_8 decimal_right: STRING_8 rounded_up: INTEGER_32 l_decimal, unrounded_dec, rounded_dec, carry_dec: DECIMAL zero_pad: STRING_8 do create Result.make_empty l_decimal := round_to (digits) digit_list := l_decimal.precise_out.split (Decimal_char.to_character_32) decimal_left := digit_list [1] Result.append (decimal_left) Result.append (create {STRING_8}.make_filled (Decimal_char, 1)) if digit_list.count > 1 then decimal_right := digit_list [2] if decimal_right.count < digits then create zero_pad.make_filled ('0', digits - decimal_right.count) Result.append (decimal_right) Result.append (zero_pad) else Result.append (decimal_right.substring (1, digits)) if decimal_right.count > digits and then decimal_right.substring (digits + 1, digits + 1).to_integer >= 5 then rounded_up := decimal_right.substring (digits, digits).to_integer + 1 if rounded_up < 10 then Result.remove_tail (1) Result.append_integer (rounded_up) else unrounded_dec := create {attached DECIMAL}.make_from_string (Result) create zero_pad.make_filled ('0', digits - 1) zero_pad.prepend ("0.") zero_pad.append ("1") carry_dec := create {attached DECIMAL}.make_from_string (zero_pad) rounded_dec := unrounded_dec + carry_dec create Result.make_from_string (rounded_dec.out) end end end else create zero_pad.make_filled ('0', digits) Result.append (zero_pad) end end set_precision (precision: INTEGER_32) -- sets the precision of Current for future operations require precision_positive: precision > 0 local l_decimal_ctx: MA_DECIMAL_CONTEXT do create l_decimal_ctx.make (precision, ma_decimal_ctx.rounding_mode) create ma_decimal.make_from_string_ctx (item, l_decimal_ctx) ma_decimal_ctx := l_decimal_ctx ma_decimal.set_shared_decimal_context (l_decimal_ctx) item := decimal_2_value_string end reset_precision -- resets the precision of Current for future operations to Default_precision local l_decimal_ctx: MA_DECIMAL_CONTEXT do create l_decimal_ctx.make (Default_precision, ma_decimal_ctx.rounding_mode) create ma_decimal.make_from_string_ctx (item, l_decimal_ctx) ma_decimal_ctx := l_decimal_ctx ma_decimal.set_shared_decimal_context (l_decimal_ctx) item := decimal_2_value_string end get_precision: INTEGER_32 -- returns the current number of digits being kept by the number do Result := ma_decimal_ctx.digits.twin end feature {DECIMAL} -- private fields item: STRING_8 -- String representation of the number Default_precision: INTEGER_32 = 36 -- the minimum accuracy of division results; -- division results will be accurate to AT LEAST this many digits. Default_rounding: INTEGER_32 = 4 -- Use half-round-up rounding Epsilon: STRING_8 = "0.000000000000001" -- for root computation Decimal_char: CHARACTER_8 = '.' Exponent_char: CHARACTER_8 = 'E' ma_decimal_ctx: MA_DECIMAL_CONTEXT -- underlying implementation ma_decimal: MA_DECIMAL -- ma_decimal is the underlying implementation make_from_decimal (decimal: MA_DECIMAL) -- create DECIMAL_WRAPPER from MA_DECIMAL do create ma_decimal.make_copy (decimal) ma_decimal_ctx := ma_decimal.shared_decimal_context item := decimal_2_value_string end decimal_ctx_for_value: MA_DECIMAL_CONTEXT -- create MA_DECIMAL_CONTEXT configured according to VALUE do create Result.make (Default_precision, Default_rounding) end decimal_setup (s: STRING_8; dv: INTEGER_32): MA_DECIMAL -- create new MA_DECIMAL object from value "s" with precision "dv" require non_void: s /= Void non_empty: not s.is_empty has_correct_format: ensurevalid (s) do ma_decimal_ctx := decimal_ctx_for_value ma_decimal_ctx.set_digits (dv) create Result.make_from_string_ctx (s, ma_decimal_ctx) Result.set_shared_decimal_context (ma_decimal_ctx) end clone_me: DECIMAL local l_s: STRING_8 do create l_s.make_from_string (item) create Result.make_from_decimal (ma_decimal) end decimal_2_value_string: STRING_8 -- converts scientific_notation to value.precise_out -- *should be deprecated once MA_DECIMAL allows precise string representation local str_list: LIST [STRING_8] coefficient_list: LIST [STRING_8] decimal_left: STRING_8 decimal_right: STRING_8 coefficients: STRING_8 zero_pad: STRING_8 l_exponent: STRING_8 pad_count: INTEGER_32 int_exponent: INTEGER_32 decimal_count: INTEGER_32 index: INTEGER_32 do if ma_decimal.is_zero then Result := "0" else str_list := ma_decimal.to_scientific_string.split (Exponent_char.to_character_32) coefficients := str_list [1] create Result.make_empty if str_list.count > 1 then l_exponent := str_list [2] int_exponent := l_exponent.to_integer coefficient_list := coefficients.split (Decimal_char.to_character_32) if coefficient_list.count > 1 then decimal_count := coefficient_list [2].count else decimal_count := 0 end if int_exponent.abs >= decimal_count then Result.append (coefficient_list [1]) if decimal_count > 0 then Result.append (coefficient_list [2]) end create zero_pad.make_empty if int_exponent > 0 then zero_pad.make_filled ('0', int_exponent - decimal_count) Result.append (zero_pad) else zero_pad.make_filled ('0', int_exponent.abs - 1) Result.prepend (zero_pad) Result.prepend ("0.") end elseif int_exponent < 0 then Result.append ("0") Result.append (create {STRING_8}.make_filled (Decimal_char, 1)) pad_count := int_exponent.abs - coefficient_list [1].count create zero_pad.make_filled ('0', pad_count) Result.append (zero_pad) Result.append (coefficient_list [1]) Result.append (coefficient_list [2]) else if int_exponent = 0 then Result := coefficients else decimal_left := coefficient_list [2].substring (1, int_exponent) decimal_right := coefficient_list [2].substring (int_exponent, decimal_count) Result.append (coefficient_list [1]) Result.append (decimal_left) Result.append (create {STRING_8}.make_filled (Decimal_char, 1)) Result.append (decimal_right) end end else coefficient_list := coefficients.split (Decimal_char.to_character_32) if coefficient_list.count > 1 and then zero.is_equal (create {DECIMAL}.make_from_string (coefficient_list [2])) then Result := coefficient_list [1] else Result := coefficients end end if Result.has (Decimal_char) then from index := Result.count until not (Result.at (index) ~ '0') loop index := index - 1 Result := Result.substring (1, index) end end end end value_precise_out: STRING_8 -- Precise string representation of object do Result := item.twin end value_out: STRING_8 -- String representation of object upto 2 decimal places do Result := precise_out_to (2) end 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) = Decimal_char 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 integer_exp (val: DECIMAL; p: INTEGER_32): DECIMAL -- recursively calculate 'val' to positive integer power 'power' -- Fast exponentiation, capped to 500 digits of precision local square: DECIMAL do if p = 0 then Result := create {attached DECIMAL}.make_from_string ("1") else square := val * val square.set_precision (500) Result := integer_exp (square, p // 2) if p \\ 2 = 1 then Result := Result * val end end Result.set_precision (500) end real_exp (val: DECIMAL; p: DECIMAL): DECIMAL -- recursively calculate 'val' to real power 'power' -- Fast exponentiation until power < 1. Then binary search on fractional exponent -- Capped to 500 decimals of precision local temp: DECIMAL low: DECIMAL high: DECIMAL mid: DECIMAL l_root: DECIMAL acc: DECIMAL two: DECIMAL do two := create {DECIMAL}.make_from_string ("2.0") if p >= one then temp := real_exp (val, p / two) temp.set_precision (500) Result := temp * temp else from low := zero high := one l_root := val.sqrt acc := l_root mid := high / two until ((mid - p).absolute <= create {DECIMAL}.make_from_string (Epsilon)) loop l_root := l_root.sqrt if mid <= p then low := mid acc := acc * l_root else high := mid acc := acc * (one / l_root) end mid := (low + high) / two end Result := acc end Result.set_precision (500) end make_from_string_ctx (value_string: STRING_8; ctx: MA_DECIMAL_CONTEXT) -- Make a new decimal from value_string relative to ctx do create ma_decimal.make_from_string_ctx (value_string, ctx) ma_decimal_ctx := ctx item := decimal_2_value_string end calculate_new_precision (other: DECIMAL): INTEGER_32 -- Used in primary operations to calculate new precision -- The new precision is the largest value of: Current, other, Default_precision, -- or the sum of Current's precision + other's precision. local curr_dig, other_dig, str_digs, digits: INTEGER_32 curr_string, other_string: STRING_8 do curr_string := Current.precise_out other_string := other.precise_out curr_dig := curr_string.count other_dig := other_string.count str_digs := curr_dig + other_dig digits := Default_precision if digits < ma_decimal_ctx.digits then digits := ma_decimal_ctx.digits end if digits < other.ma_decimal_ctx.digits then digits := other.ma_decimal_ctx.digits end if digits < str_digs then digits := str_digs end Result := digits end invariant consistent_rounding: Default_rounding = {MA_DECIMAL_CONTEXT}.round_half_up note info: "[ For an oracle see http://delaneyrm.com/MPCalcRB.html. test: BOOLEAN local d1, d2, d3, d4, d5, d6: DECIMAL r64: REAL_64 i: INTEGER r: RATIONAL do comment ("test: Mortgage Calculation in REAL_64 vs DECIMAL") -- floating point arithmetic cannot distinguish -- between 0.4 and 0.40000000000000002 r64 := 0.1 + 0.3 Result := r64 = 0.4 and r64 = 0.40000000000000002 check Result end -- decimal arithmetic can d1 := "0.1" ; d2 := "0.3" d3 := d1 + d2 Result := d3 ~ "0.4"and d3 /~ "0.40000000000000002" check Result end assert_equal ("precise_out test", "0.4", d3.precise_out) assert_equal ("out test1", "0.40", d3.out) r64 := 100000*(1+0.05/12)^360 Result := r64 = 446774.43140061089 check Result end d1 := "100000" d2 := "0.05"; d3 := "12"; d4 := "360" d2.set_precision(120) d5 := d1 * (d1.one + d2/d3)^d4 Result := d5.precise_out.starts_with ("446774.431400613221242807011041301589597772674487270698002439224547330881614835011308578390042190134313238565062087470576") check Result end assert_equal ("out test2", "446774.43", d5.out) d6 := d5 - "446774.43140061089" Result := d6.precise_out.starts_with ("0.000000002331242807011041301589597772674487270698002439224547330881614835011308578390042190134313238565062087470576") check Result end end ]" end -- class DECIMAL
Generated by ISE EiffelStudio