Help with rounding and formatting of Zarith rationals

I’ve been using Zarith with great success, as it’s really fast and easy to work with (once you get going).
But lately I’ve been having some problems with my users reporting “wrong numbers” where, as I found out, it’s because of my rounding/printing of the end result, and not because of wrong math.

For now I’ve been relying on the built in ways for printing. At least the ones that were obvious to me and ones I have been advised on using in the Discord channel.

Here is an example:

utop # Format.asprintf "%g" Q.(to_float (3870411 // 50) );;
- : string = "77408.2"

utop # Format.asprintf "%f" Q.(to_float (3870411 // 50) );;
- : string = "77408.220000"

Is there a good way to print the actual result which is 77408.22 without introducing unnecessary complexity to my software?

If by actual result, you mean to print exactly decimal number, one solution might be to compute the required precision on the fly:

let rec valuation prim v x =
  let q, r = Z.ediv_rem x base in 
  if Z.zero = r then valuation prim (1+v) q else v, q;;
let valuation prim x = valuation prim 0 x

let required_precision non_decimal =
  let v2, x = valuation (Z.of_int 2) x in
  let v5, x = valuation (Z.of_int 5) x in
  if x = Z.one then max v2 v5
  else non_decimal

let pp_decimal ppf x =
  Format.fprintf ppf "%.*f" 
    (required_precision 18 @@ Q.den x)
    (Q.to_float x) 

The toplevel itself is using “%.*g” with precision 12, 15 and 18, and pick the first precision for which the printed string is equal to the original float.

2 Likes

Before going to the length of octachron’s answer, you could try first a %.12g or %.15g format for printf. Unlike f formats, g formats eliminate useless trailing zeroes after the decimal point (as in your second example). 12 or 15 decimals (at most) is often enough to print the FP number correctly, yet small enough that it doesn’t create spurious non-zero decimals.

If you need a more comprehensive answer, you shoud first explain what you mean by “print the actual result”. Many rational numbers have infinite decimal representations (think 1/3). How do you want them printed?

1 Like

Sorry for not being more specific. I meant what @octachron presumed: Print the actual number when there is a proper (non-infinite) decimal representation, as is in this case.

Of course, I’d like to be able to specify precision for continued fractions when needed, but currently my software can’t reach such numbers because of business reasons (only addition of decimal numbers with exact representation)

So, what you suggest is probably the proper solution for now, but later I might resort to octachron’s suggestion.

Thanks both!