Is there a lib that makes it easy to read and write floats in a binary representation, e.g. where 0.011 means 0.375 ?
(This is not a question about reading and writing files. I’m more interested in embedding binary floats in source or even typing them at a utop prompt.)
I’m assuming you don’t care about the actual base but about floating point binary layout specification. OCaml has built-in support for human readable specification of the binary layout of floats in hexadecimal notation.
For writing. Since 4.03 you can use the %hformat specifier. Before that you you can use Gg.Float.pp whose doc string details the format.
For reading. float_of_string, which relies on the C strod function, will read back these representation on all OCaml versions.
A subset of the format can also be directly specified in OCaml sources, see the OCaml manual here for details.
Thanks @dbuenzli. I see that question was not clear enough. I don’t care at all about floating point binary layout within OCaml.
# Printf.printf "%h\n" 0.125;;
0x1p-3
But I would like “0.001” to be printed in response to an OCaml float 0.125, and I would like to be able to give “0.001” to a function and get back the OCaml float 0.125. It looks to me like strod and therefore float_of_string only know about decimal format, and hex format using “0x”.
(It looks like the value returned with “%h” conveys the information that I want from a representation of a float, but isn’t as easy to read. I did not see a way that fprintf allows replacing the “p-3” with “.00” in the printed output, but I may have missed some trick.)
Thanks @Chet_Murthy. I probably won’t do it right away, but that’s helpful. Then again, you are describing a pretty simple hack. I might do it. No need to actually understand much. But the pp source @dbuenzli pointed me to doesn’t look that difficult, either.
(I want an arbitrary number of digits, but I wouldn’t expect that that would be a difficulty.)
let tobin f =
let s = Printf.sprintf "%h" f in
let s = String.sub s 2 ((String.length s) - 2) in
let [mant;exp] = String.split_on_char 'p' s in
let exp = int_of_string exp in
let mant,morexp = match String.split_on_char '.' mant with
[mant] -> mant,exp
| [a;b] -> a^b, exp - 4 * (String.length b) in
let tobits = function
| '0' -> "0000"
| '1' -> "0001"
| '2' -> "0010"
| '3' -> "0011"
| '4' -> "0100"
| '5' -> "0101"
| '6' -> "0110"
| '7' -> "0111"
| '8' -> "1000"
| '9' -> "1001"
| 'a' -> "1010"
| 'b' -> "1011"
| 'c' -> "1100"
| 'd' -> "1101"
| 'e' -> "1110"
| 'f' -> "1111" in
let tobits_seq c = c |> tobits |> String.to_seq in
let mantbits = mant |> String.to_seq |> Seq.flat_map tobits_seq |> String.of_seq in
let fin =
if morexp >= 0 then mantbits^(String.make morexp '0')^"."
else
let pad_mantbits =
let mantlen = String.length mantbits in
if mantlen >= -morexp then mantbits else (String.make ((-morexp)-mantlen) '0')^mantbits in
let padmantlen = String.length pad_mantbits in
(String.sub pad_mantbits 0 (padmantlen - -morexp))^"."^(String.sub pad_mantbits (padmantlen - -morexp) (-morexp))
in
let finlen = String.length fin in
let fin = match String.index_opt fin '1', String.index_opt fin '.' with
| Some onepos, Some dotpos when onepos < dotpos -> String.sub fin onepos (finlen - onepos)
| None, Some dotpos when dotpos > 1 -> String.sub fin (dotpos - 1) (finlen - (dotpos - 1))
| _ -> fin
in fin
;;
val tobin : float -> string = <fun>
# tobin 0. ;;
- : string = "0."
# tobin 0.125 ;;
- : string = "0.001"
# tobin 0.03125 ;;
- : string = ".00001"
# tobin 256. ;;
- : string = "100000000."
#
Think I fixed stripping superfluous leading zeroes.