Esta crónica ha sido aprobado por expertos así se garantiza la veracidad de esta noticia.
Solución:
La razón principal para transferir valores numéricos en JSON como cadenas es eliminar cualquier pérdida de precisión o ambigüedad en la transferencia.
Es true que la especificación JSON no especifica una precisión para valores numéricos. Esto no significa que los números JSON tengan una precisión infinita. Significa que no se especifica la precisión numérica, lo que significa que las implementaciones JSON son libres de elegir la precisión numérica que sea conveniente para su implementación o sus objetivos. Es esta variabilidad la que puede resultar complicada si su aplicación tiene requisitos de precisión específicos.
La pérdida de precisión generalmente no es evidente en la codificación JSON del valor numérico (1.7 es agradable y conciso) pero se manifiesta en el análisis JSON y las representaciones intermedias en el extremo receptor. Una función de análisis JSON analizaría razonablemente 1.7 en un número de punto flotante de doble precisión IEEE. Sin embargo, las representaciones decimales de longitud finita / precisión finita siempre se encontrarán con números cuyas expansiones decimales no se pueden representar como una secuencia finita de dígitos:
-
Números irracionales (como pi y e)
-
1.7 tiene una representación finita en notación de base 10, pero en notación binaria (base 2), 1.7 no se puede codificar exactamente. Incluso con un número casi infinito de dígitos binarios, solo se acercará a 1.7, pero nunca llegará exactamente a 1.7.
Por lo tanto, analizar 1.7 en un número de punto flotante en la memoria y luego imprimir el número probablemente arrojará algo como 1.69, no 1.7.
Los consumidores del valor JSON 1.7 podrían usar técnicas más sofisticadas para analizar y retener el valor en la memoria, como usar un tipo de datos de punto fijo o un “string int “con precisión arbitraria, pero esto no eliminará por completo el espectro de la pérdida de precisión en la conversión de algunos números. Y la realidad es que muy pocos analizadores JSON se molestan con medidas tan extremas, ya que los beneficios para la mayoría de las situaciones son bajos y los costos de memoria y CPU son altos.
Entonces, si desea enviar un valor numérico preciso a un consumidor y no desea la conversión automática del valor en la representación numérica interna típica, su mejor opción es enviar el valor numérico como un string y decirle al consumidor exactamente cómo string debe procesarse si es necesario realizar operaciones numéricas en él.
Por ejemplo: en algunos productores de JSON (JRuby, por ejemplo), los valores de BigInteger se envían automáticamente a JSON como cadenas, en gran parte porque el rango y la precisión de BigInteger es mucho mayor que el flotante de doble precisión IEEE. Reducir el valor de BigInteger al doble para generar una salida como un valor numérico JSON a menudo perderá dígitos significativos.
Además, la especificación JSON (http://www.json.org/) establece explícitamente que los NaN y los infinitos (INF) no son válidos para los valores numéricos JSON. Si necesita expresar estos elementos marginales, no puede usar el número JSON. Tienes que usar un string o estructura de objeto.
Finalmente, hay otro aspecto que puede llevar a elegir enviar datos numéricos como cadenas: control del formato de visualización. Los ceros iniciales y los ceros finales son insignificantes para el valor numérico. Si envía el valor de número JSON 2.10 o 004, después de la conversión a forma numérica interna, se mostrarán como 2.1 y 4.
Si está enviando datos que se mostrarán directamente al usuario, probablemente desee que sus cifras de dinero se alineen bien en la pantalla, alineadas con decimales. Una forma de hacerlo es hacer que el cliente sea responsable de formatear los datos para su visualización. Otra forma de hacerlo es hacer que el servidor formatee los datos para mostrarlos. Quizás sea más sencillo para el cliente mostrar cosas en la pantalla, pero esto puede hacer que la extracción del valor numérico del string difícil si el cliente también necesita hacer cálculos sobre los valores.
Seré un poco contrario y diré eso 7.47
es perfectamente seguro en JSON, incluso por importes económicos, y que "7.47"
no es más seguro.
Primero, permítanme abordar algunos conceptos erróneos de este hilo:
Por lo tanto, analizar 1.7 en un número de punto flotante en la memoria y luego imprimir el número probablemente arrojará algo como 1.69, no 1.7.
Eso no es true, especialmente en el contexto del formato de doble precisión IEEE 754 que se mencionó en esa respuesta. 1.7 se convierte en un doble exacto 1.6999999999999999555910790149937383830547332763671875 y cuando ese valor se “imprime” para su visualización, siempre ser 1,7, y nunca 1,69, 1,699999999999 o 1,70000000001. Es 1,7 “exactamente”.
Aprende más aquí.
7.47 puede ser en realidad 7.4699999923423423423 cuando se convierte en flotante
7.47 ya es un flotante, con un valor doble exacto 7.46999999999999975131004248396493494510650634765625. No se “convertirá” en ningún otro flotador.
un sistema simple que simplemente trunca los dígitos adicionales resultará en 7.46 y ahora ha perdido un centavo en alguna parte
IEEE redondea, no trunca. Y no se convertiría a ningún otro número que no sea 7,47 en primer lugar.
¿Es el número JSON realmente un flotante? Según tengo entendido, es un número independiente del idioma, y puede analizar un número JSON directamente en un BigDecimal java u otro formato de precisión arbitrario en cualquier idioma si así lo desea.
Se recomienda que los números JSON se interpreten como dobles (formato de doble precisión IEEE 754). No he visto un analizador que no esté haciendo eso.
Y no, BigDecimal(7.47)
no es la forma correcta de hacerlo, en realidad creará un BigDecimal que representa la exacta doble de 7,47, que es 7,46999999999999975131004248396493494510650634765625. Para obtener el comportamiento esperado, BigDecimal("7.47")
debería ser usado.
En general, no veo ningún problema fundamental con "price": 7.47
. Se convertirá en un doble en prácticamente todas las plataformas, y la semántica de IEEE 754 garantiza que se “imprimirá” como 7.47 exactamente y siempre.
Por supuesto, los errores de redondeo de punto flotante pueden ocurrir en cálculos posteriores con ese valor, consulte, por ejemplo, 0.1 + 0.2 == 0.30000000000000004, pero no veo cómo las cadenas en JSON mejoran esto. Si “7.47” llega como string y debería ser parte de algún cálculo, deberá convertirse a algún tipo de datos numérico de todos modos, probablemente flotante :).
Vale la pena señalar que las cadenas también tienen desventajas, por ejemplo, no se pueden pasar a Intl.NumberFormat
, no son un tipo de datos “puro”, por ejemplo, el punto es una decisión de formato.
No estoy muy en contra de las cuerdas, también me parecen bien, pero no veo nada malo en "price": 7.47
cualquiera.
Versión resumida
Solo citando la respuesta de @ dthorpe, ya que creo que este es el punto más importante:
Además, la especificación JSON (http://www.json.org/) establece explícitamente que los NaN y los infinitos (INF) no son válidos para los valores numéricos JSON. Si necesita expresar estos elementos marginales, no puede usar el número JSON. Tienes que usar un string o estructura de objeto.
Si te gusta la informática, eres capaz de dejar un escrito acerca de qué le añadirías a esta noticia.