Quería obtener la precisión de un número decimal para mi aplicación, probé varios métodos pero no pude obtener lo que quería.

Al principio, traté de convertir el flotador en una cadena, y hacer una división simple y almacenarlo en una lista de entradas, luego obtengo la longitud de la lista

let a = 12.12345678910111213141516;;
let stringA = string_of_float a;;

let list_of_ints stringA = List.map int_of_string (Str.split (Str.regexp "\.") stringA);;
let stringList = list_of_ints stringA;;
let thedecimal = string_of_int (List.nth stringList 1);;

S.length thedecimal;;

El problema es que cuando tengo más de 10 números en el lado característico de mi flotador, no funciona, siempre devuelve 10

val a : float = 12.1234567891011125
val stringA : string = "12.1234567891"
val list_of_ints : string -> int list = <fun>
val stringList : int list = [12; 1234567891]
val thedecimal : string = "1234567891"
- : int = 10

Mi segundo método estaba tratando de hacer una resta entre el número flotante y su característica, de modo que obtengo la mantisa, luego la coloco en una lista y obtengo la longitud menos 2 (0 y el.)

let b = int_of_float a;;
let c = a -. (float_of_int b);;
S.length (string_of_float c)-2;; 

Devuelve 12, lo cual es extraño porque espero un 23,

val b : int = 12
val c : float = 0.123456789101112463
- : int = 12

Soy nuevo con Ocaml. Si alguien tiene alguna solución sobre cómo obtener la precisión, necesito su ayuda, gracias.

2
Yassine 13 dic. 2016 a las 13:38

2 respuestas

La mejor respuesta

Aparentemente, string_of_float decidió limitar el número de dígitos significativos mostrados a doce (tendría que investigarlo para estar seguro).

En realidad, esto es algo bueno, porque un doble podría representarse como un número bastante grande.

# let f = 0.123456789012345678901234567890;;   
val f : float = 0.123456789012345677
# let s = string_of_float f;;
val s : string = "0.123456789012"
# let s' = Printf.sprintf "%.10f" f;;
val s' : string = "0.1234567890"
# let s'' = Printf.sprintf "%.30f" f;;
val s'' : string = "0.123456789012345677369886232100"
# let s''' = Printf.sprintf "%.60f" f;;
val s''' : string =
  "0.123456789012345677369886232099815970286726951599121093750000"
(* longer than this one just add zeroes *)

Ahora, estás pidiendo un número de precisión real. Según Wikipedia:

  • Bit de signo: 1 bit
  • Exponente: 11 bits
  • Significativa y precisión: 53 bits (52 almacenados explícitamente)

Y además...

El formato se escribe con el significado que tiene un bit entero implícito de valor 1 (excepto para datos especiales, consulte la codificación del exponente a continuación). Con los 52 bits del significado de fracción apareciendo en el formato de memoria, la precisión total es por lo tanto de 53 bits (aproximadamente 16 dígitos decimales, 53 log10 (2) ≈ 15.955).

Entre 252 = 4,503,599,627,370,496 y 253 = 9,007,199,254,740,992 los números representables son exactamente los enteros. Para el siguiente rango, de 253 a 254, todo se multiplica por 2, por lo que los números representables son los pares, etc. A la inversa, para el rango anterior de 251 a 252, el espaciado es 0.5, etc.

El espaciado como fracción de los números en el rango de 2n a 2n + 1 es 2n − 52. El error de redondeo relativo máximo cuando se redondea un número al representable más cercano (la máquina épsilon) es por lo tanto 2−53.

Entonces lo tienes, tu precisión es de alrededor de 2^-53 con respecto al valor de tu flotante. Esto le da alrededor de 15 o 16 dígitos decimales de precisión para cualquier flotante.

Como nota al margen, si está usando expresiones regulares en un flotador, debe calmarse y pensar en los gatitos por un minuto.

1
PatJ 13 dic. 2016 a las 12:25

Debido al número limitado de bits en la representación de dobles, la precisión máxima es 324. Consulte Wikipedia.

Al comprometer la precisión, la representación subnormal permite valores incluso más pequeños, hasta aproximadamente 5 × 10−324.

La siguiente función es lo que quieres.

let precision_of_float f = 
  let (fractional, _) = modf f in
  let fractional_string = (Printf.sprintf "%.324f" fractional) in 

  (* remove ending zeros *)
  let fractional_string = Str.replace_first (Str.regexp "0+$") "" fractional_string in

  (* fractional_string is of the form 0.[0-9]* *)
  String.length fractional_string - 2
1
Xuan Tung Vu 14 dic. 2016 a las 10:07