From: Kwon, J. Y. <jim...@ng...> - 2007-02-15 01:43:54
|
Gobo Developers Mailing List: There's a small precision problem in using ST_FORMATTING_ROUTINES to format doubles, e.g.: test_0001 is -- Test procedure. local str_fmt: ST_FORMATTING_ROUTINES d: DOUBLE do d :=3D 941.65899999999817 print( "d1=3D" + d.out + "%N" ) print( "d2=3D" + str_fmt.format_single( "$11.9f", str_fmt.double_cell( d ) ) + "%N" ) end -- test_local Running the above test procedure will produce the results: d1=3D941.65899999999817 d2=3D942.589999910 'd2' is bizarre and unexpected. The problem area is in ST_DOUBLE_FORMATTER (under the string/formatter/parameter/st_double_formatter.e), in the routine 'build_integer_and_fractional_parts'. The developer comment in the routine mentions a fix to handle large precisions (e.g., 8 or more decimal places) by handling the fractional part of the original value into "pieces" of 8 digits. Unfortunately, this fix didn't quite handle rounding issues correctly. Here's the old code: build_integer_and_fractional_parts (d: DOUBLE) is -- Build `integer_part', `fractional_part' and `fsign' for `d'. --=20 -- This function entered as replacing of simple expression: -- create f_f.make (floor((fractional_part*10^(precision))+0.5)) -- which fails in case of large precision. -- Now we obtain fraction part with pieces of decimal_digit_count digits. local i: INTEGER fa: DOUBLE fp, fff: DOUBLE do fa :=3D d.abs fsign :=3D double_sign (d) STRING_.wipe_out (fractional_part) integer_part :=3D DOUBLE_.floor_to_integer (fa) from i :=3D precision fp :=3D fa - integer_part until i <=3D decimal_digit_count - 1 loop fff :=3D fp * (10.0 ^ decimal_digit_count) -- .truncated_to_real append_integer_to_string (DOUBLE_.floor_to_integer (fff), decimal_digit_count, fractional_part) fp :=3D fff - fff.floor i :=3D i - decimal_digit_count end append_integer_to_string (DOUBLE_.floor_to_integer ((fp * 10.0 ^ i) + 0.5), i, fractional_part) check new_count: fractional_part.count =3D precision or fractional_part.count =3D precision + 1 end if fractional_part.count =3D precision then integer_part :=3D integer_part * fsign else -- There is overflow. -- We should add 1 to the int_part ... integer_part :=3D (integer_part + 1) * fsign -- ... and remove extraneous '1' fractional_part.remove (1) end if not fractional_part.is_empty then fractional_part.precede ('.') end end Here's a temporary fix, until perhaps one of the Gobo Eiffel library developers can come up with a proper (more elegant?) solution (I've left in parts of the old code commented out for reference). The fix stores the "pieces" of 8 fractional digits in a temporary list, rounds the last "piece," back-propagates the "overflow" carry through the temporary list if necessary, then finally assembles the fractional digits string. If the entire fractional part overflowed, then the integer portion is incremented as well: build_integer_and_fractional_parts (d: DOUBLE) is -- Build `integer_part', `fractional_part' and `fsign' for `d'. -- -- This function entered as replacing of simple expression: -- create f_f.make (floor((fractional_part*10^(precision))+0.5)) -- which fails in case of large precision. -- Now we obtain fraction part with pieces of decimal_digit_count digits. -- 02/14/2007 JYK - Added correct handling of those "pieces." local i: INTEGER fa: DOUBLE fp, fff: DOUBLE fff_list: DS_BILINKED_LIST[ INTEGER ] -- Holds fractional part pieces rounded_int: INTEGER overflow_int: INTEGER is_overflow_consumed: BOOLEAN do create fff_list.make fa :=3D d.abs fsign :=3D double_sign (d) STRING_.wipe_out (fractional_part) integer_part :=3D DOUBLE_.floor_to_integer (fa) from i :=3D precision fp :=3D fa - integer_part until i <=3D decimal_digit_count - 1 loop fff :=3D fp * (10.0 ^ decimal_digit_count) -- .truncated_to_real -- append_integer_to_string (DOUBLE_.floor_to_integer (fff), decimal_digit_count, fractional_part) fff_list.put_last (DOUBLE_.floor_to_integer (fff)) fp :=3D fff - fff.floor i :=3D i - decimal_digit_count end rounded_int :=3D DOUBLE_.rounded_to_integer (fp * 10.0 ^ i) overflow_int :=3D DOUBLE_.truncated_to_integer (10 ^ i) if rounded_int >=3D overflow_int then -- The final digits have "overflowed" - reinitialize 'rounded_int' -- and propagate the carry through the previous fractional digit -- pieces. rounded_int :=3D 0 from fff_list.finish overflow_int :=3D DOUBLE_.truncated_to_integer (10 ^ decimal_digit_count) until is_overflow_consumed or fff_list.off loop fff_list.replace_at (fff_list.item_for_iteration + 1) if fff_list.item_for_iteration < overflow_int then -- No more overflow - all done. is_overflow_consumed :=3D True else -- Current digits have "overflowed" - reset the digits and -- continue propagating the carry. fff_list.replace_at (0) end fff_list.back end else -- No overflow condition. is_overflow_consumed :=3D True end -- Build up the fractional digits string from the digit pieces in -- 'fff_list' and 'rounded_int' (containing the final digits). from fff_list.start until fff_list.off loop append_integer_to_string (fff_list.item_for_iteration, decimal_digit_count, fractional_part) fff_list.forth end -- append_integer_to_string (DOUBLE_.floor_to_integer ((fp * 10.0 ^ i) + 0.5), i, fractional_part) append_integer_to_string ( rounded_int, i, fractional_part ) -- check new_count: fractional_part.count =3D precision or fractional_part.count =3D precision + 1 end -- if fractional_part.count =3D precision then if is_overflow_consumed then integer_part :=3D integer_part * fsign else -- There is overflow. -- We should add 1 to the int_part ... integer_part :=3D (integer_part + 1) * fsign -- -- ... and remove extraneous '1' -- fractional_part.remove (1) end if not fractional_part.is_empty then fractional_part.precede ('.') end end Running the above test procedure with the fix produces the expected results: d1=3D941.65899999999817 d2=3D941.659000000 Other test cases: d :=3D 941.99999999999817 produces the expected: d1=3D941.99999999999818 d2=3D942.000000000 I haven't done exhaustive testing on this, but the temporary fix is enough of a workaround for me at the moment. I hope this was helpful. -Jim Jimmy Y. Kwon Northrop Grumman Mission Systems 222 W. 6th St., San Pedro, CA 90731 (310) 831-0611 x2209 -----Original Message----- From: Eric Bezault [mailto:er...@go...]=20 Sent: Wednesday, February 14, 2007 4:31 PM To: Kwon, Jimmy Y. Cc: Gobo Developers Mailing List Subject: Re: Gobo Eiffel Library 3.4/3.5: Bug Found Hi Jim, Kwon, Jimmy Y. wrote: > Eric Bezault, Gobosoft: >=20 > I'm a software engineer at Northrop Grumman, using Eiffel in my project. >=20 > I've found a code bug within Gobo Eiffel Library version 3.5 (the same > bug exists in version 3.4). The class with the problem is > ST_DOUBLE_FORMATTER. Although I've (at least temporarily) fixed the > problem for myself, I was wondering if there was an official way to > inform Gobosoft of the problem, steps to reproduce the problem, etc., so > that a fix could be released in the next official release. >=20 > I could also provide source code containing the (temporary) fix if you > wish. Thank goodness for open source libraries. We don't use SourceForge's bug tracker yet, although we will use it soon. In the meantime you can report the bug (with the possible fix) to the developer mailing list: <gob...@li...> --=20 Eric Bezault mailto:er...@go... http://www.gobosoft.com |