我正在使用Zarith库进行任意精度的有理算术。假设我有一个有理数q
类型Q.t
,它是两个大整数的比率(Q
是 Zarith 的任意精度有理数模块)。有时,为了便于阅读,我想将此数字打印为浮点数,有时我需要将此数字转换为浮点数,以便以后进行非任意精度计算。有没有办法将q
浮点数转换为一定精度?
我现在转换q
为浮点的方式没有任何保证,并且可以创建未定义的浮点数(Z
是任意精度整数模块):
let to_float q =
let n, d = num q, den q in
(* check if d is zero and raise an error if it is *)
let nf, df = Z.to_float n, Z.to_float d in
nf /. df
有没有更好的方法来处理这个问题,我可以获得一个最准确地近似 any 的浮点数q
?
编辑
如果有人感兴趣,我很快在 OCaml 中写下了 Mark Dickinson 的答案。它可能(肯定)可以改进和清理。如果我这样做或者如果有人有任何改进建议,我会进行编辑。但是现在这已经解决了我的问题!
let to_float q =
let n, d = num q, den q in
let n_sign = Z.sign n in
let d_sign = Z.sign d in (* always >= 0 *)
if d_sign = 0 then raise Division_by_zero;
let n = Z.abs n in
if n_sign = 0 then 0. else
let shift = (Z.numbits n) - (Z.numbits d) - 55 in
let is_subnormal = shift < -1076 in
let shift = if is_subnormal then -1076 else shift in
let d = if shift >= 0 then Z.shift_left d shift else d in
let n = if shift < 0 then Z.shift_left n (-shift)
else n in
let quotient, remainder = Z.div_rem n d in
let quotient = if (Z.compare remainder (Z.zero)) = 0 && Z.is_even quotient then
Z.add Z.one quotient else quotient in
let quotient = if not is_subnormal then quotient else
let round_select = Z.to_int @@ Z.rem quotient @@ Z.of_int 8 in
Z.add quotient [|Z.zero;Z.minus_one;Z.of_int (-2);Z.one;Z.zero
;Z.minus_one;Z.of_int 2;Z.one|].(round_select)
in
let unsigned_res = ldexp (Z.to_float quotient) shift in
if n_sign = 1 then unsigned_res else -.unsigned_res
稍后我会考虑为 GMP 的mpq_get_d
功能编写一个接口,但我不完全确定该怎么做。我看到如何做到这一点的唯一方法是将其转换q : Q.t
为字符串并将其传递给:
int mpq_set_str (mpq_t rop, const char *str, int base)
有谁知道如何在 OCaml 中传递rop
或mpq_get_d
有描述如何执行此操作的参考?我浏览了 RWO 的第 19 章,并没有看到这样的情况。