Todos hemos aprendido acerca de los números de punto flotante en la escuela. A diferencia de los números enteros que no tienen puntos decimales.
No es necesario ser un genio de las matemáticas para saber que el número cinco es un número entero, mientras que 5,43 es un número de punto flotante. Claramente, todos estamos familiarizados con el punto decimal que es un claro punto de diferenciación entre los dos.
Por supuesto, no sorprende que los flotantes se usen en los lenguajes de programación e incluso se clasifiquen como un tipo de datos primitivo distinto debido a lo necesario que es para el cálculo diario.
Cuando comience a aprender Python o Java, utilizará el tipo de datos flotante. Particularmente, cuando realizas la operación de división entre dos números de punto flotante.
En Python, incluso si no tiene que declarar un número de punto flotante, dividir dos de estos números puede dar como resultado un número del mismo tipo de datos, como se muestra a continuación:
También podemos producir el mismo resultado en Java, incluso si tenemos que declarar explícitamente el tipo de datos primitivo 'flotante' para todas las variables utilizadas.
Como se puede ver, este tipo de cálculo es necesario para una serie de aplicaciones, y es precisamente por eso que las operaciones con números de coma flotante están disponibles como característica en ambos lenguajes de programación.
De manera muy diferente, si ha estado aprendiendo Solidity, debería haber notado que no incluye el tipo de datos de número de punto flotante.
En cambio, si ha escrito algún código en Solidity, estaría usando números del tipo entero con signo o sin signo.
Entonces, si desea realizar el mismo cálculo, cinco dividido por dos, como se muestra en la sección anterior, así es como se verá, como parte de un contrato inteligente:
Sin embargo, el resultado que obtengas no será el mismo, ya que Solidity no soporta el tipo de datos float. Al menos, no todavía.
Específicamente, Solidity redondeará el resultado hacia cero. Lo cual, en este caso, y como se muestra arriba, dará como resultado un valor de dos. Piense en esto como la operación de módulo en ambos números.
Si bien en circunstancias normales, esto no debería importar mucho, hay momentos en que el resultado puede conducir a un error en el cálculo. Por lo que se recomienda evitar o postergar en lo posible la operación de división.
Incluso si la regla BODMAS en la escuela requiere que todos los estudiantes de matemáticas realicen el cálculo de la división antes de la multiplicación, esto no se recomienda si tiene que realizar la división de enteros en Solidity.
Descubramos por qué, con este sencillo ejemplo, se realizan multiplicaciones y divisiones con tres números:
Si ingresa los números uno, tres y cinco al implementar el contrato inteligente, debería obtener el mismo valor para las funciones getResult y getResult2, ¿verdad?
Si usa una calculadora simple, debería obtener un valor flotante de 1,666, que se traduce en uno en Solidez, gracias a la ausencia de valores flotantes.
Desafortunadamente, esto no es lo que sucede cuando verifica los resultados de las funciones getResult y getResult2, como se muestra a continuación:
Si realizamos la división primero, obtenemos un resultado final de cero. A diferencia del valor esperado de uno, cuando pospone esa operación hasta el final en la función getResult.
Como puede ver, incluso si hemos calculado este valor anticipando la ausencia de valores flotantes, todavía surge un error que puede causar una pérdida de precisión. Lo que, a su vez, puede traducirse en una pérdida económica que puede evitarse fácilmente.
Entonces, ¿cómo evitamos el error de división de enteros? Más importante aún, ¿cómo aumentamos la precisión de nuestro cálculo? Veamos las tres formas más comunes de hacer esto.
Dado que hay una serie de enfoques para eludir este error, comencemos con la solución más simple y veamos un par más antes de dar por terminado el día.
Método #1: Usa un multiplicador
Ahora, además de colocar la operación de división en último lugar, una forma de asegurarse de no terminar con errores o valores imprecisos es usar un multiplicador. En el siguiente ejemplo, usaremos un multiplicador de 100 junto con los mismos tres números usados anteriormente.
Ahora, cuando implementa el contrato con el siguiente código y llama a ambas funciones, este es el resultado:
Dado que la salida deseada es 1,666 o 166/100, podemos ver que el valor getResult2 nos proporciona la precisión necesaria cuando el multiplicador funciona junto con los tres números. Eso sí, si no usas el multiplicador como en la función getResult, obtendrás 1.
Donde 0.666 se trunca del resultado, como se esperaba de manera predeterminada cuando usa Solidity. Entonces, para recuperar este valor, todo lo que tienes que hacer es dividir el resultado por el multiplicador.
Como sabrá, Solidity se mueve hacia cero cuando se trata de redondear en el caso de enteros con y sin signo, por lo que esta solución también funciona en el caso de enteros con signo, una vez que implementa y ejecuta el código a continuación con los argumentos menos uno , tres y cinco:
Cuando se trata de los valores generados por las funciones para enteros con signo, aquí están:
Claramente, también podemos mantener la precisión para los enteros con signo, usando un multiplicador. Aunque hay una forma precisa de redondear los enteros con signo que veremos a continuación.
Método n.º 2: use la división de piso para enteros con signo
Ahora, si uno mira la línea numérica a continuación, el resultado de realizar la división de enteros entre dos enteros sin signo se redondea más cerca de cero. Como en el caso de obtener 1,666, Solidez lo redondea a 1, que es un número menor.
Sin embargo, cuando se trata de números enteros con signo, un resultado de -1,6666 se redondeará a -1, que es el mayor de los dos números. Por lo tanto, la división de piso debe aplicarse aquí en lugar de la división redondeada a cero que se implementa en Solidity de manera predeterminada. En aras de la precisión, por supuesto.
Si el tipo de datos flotante estuviera disponible, se calcularía el valor de -1.666. Si bien Solidity redondearía esto a -1, aplicar la división de piso a los enteros con signo lo reducirá a -2.
Cuando llama a las funciones getResult y getResult2, obtenemos el valor como se muestra a continuación para los argumentos menos uno, tres y cinco:
Como puede ver, getResult calcula el valor utilizando el método de redondeo hacia cero, mientras que getResult2 lo redondea al entero más pequeño aquí, en virtud de la división del suelo.
Método #3: Use la biblioteca ABDKMath64x64
Ahora, para el método final, usaremos la biblioteca ABDKMath64x64, que convierte el resultado de las operaciones de división en números de punto fijo.
Una vez más, se dice que el uso de esta biblioteca mejora la precisión en comparación con el método de redondeo hacia cero que está disponible de forma predeterminada en Solidity. Para comprender el resultado, comparemos los resultados de la suma con la función div, como se muestra a continuación:
Cuando agrega los argumentos 1, 1 y 1 al implementar el contrato inteligente, obtiene los valores, como se muestra a continuación:
No debería sorprender que la función de suma devuelva un valor entero de dos, con los tres argumentos sumando tanto. En cuanto a la función div, se devuelve un valor int 128 que representa un número de punto fijo de 64,64 bits con signo y con el que puede realizar las operaciones numéricas habituales.
Por supuesto, la biblioteca ABDKMath64x64 no es la única que se puede usar para mejorar la precisión además de evitar el error de división de enteros.
Hay varios otros, con algunos ejemplos de Fixidity , DSMath y las bibliotecas BANKEX que usan diferentes formatos de números. Formatos de número que son diferentes del formato de número de punto fijo de 64,64 bits utilizado en el ejemplo anterior. Por lo tanto, si bien estas bibliotecas pueden parecer útiles para explorar, recuerde que sus formatos de número no funcionarán con ninguna de las otras bibliotecas disponibles.