¿Cuál es una forma de obtener el orden de magnitud de un número entero (o flotante) en un contrato de solidez?
por ejemplo tal que
MyContract.getOrderMag(2564) returns 3
MyContract.getOrderMag(987) returns 2
MyContract.getOrderMag(2.3e76) returns 76
Idealmente en la menor cantidad de gas usando la forma posible...
Esto es lo que tengo:
pragma solidity ^0.4.6;
contract Magger {
function getOrderMag(int256 input) constant returns (int256){
int counter=0;
int temp = input;
while((temp/10)>1){
temp = temp/10;
counter++;
}
return counter;
}
}
Esto no funciona con números negativos o, como señaló @RichardHorrocks, cuando la entrada es 10.
Pensando un poco más en esto...
Su código toma un (firmado) int256
como entrada. El valor máximo (absoluto) de esto es alrededor de 1e+76
, lo que equivaldría a 76 ciclos del while
ciclo, cada uno compuesto por múltiples instrucciones.
En tales casos, probablemente sería más barato usar un método logarítmico , es decir, tomar el log 10 de la entrada e ignorar cualquier parte fraccionaria. (Digo "probablemente", no lo he comprobado).
Dependerá de los valores que espera input
tomar normalmente y de la rapidez con la que el algoritmo logarítmico converge en una respuesta.
Así es como lo hice. Probablemente hay margen significativo para la mejora. Aceptaría cualquier respuesta que use menos gas:
pragma solidity ^0.4.6;
contract Magger {
function getOrderMag(int256 input) constant returns (int256){
int counter=0;
if (input<0){
input=input*-1;
}
while((input/10)>=1){
input = input/10;
counter++;
}
return counter;
}
}
input
es 10, entonces counter
es 0 en lugar de 1 (debido a la while
condición).temp
variable y usarla input
en el cálculo.function magnitude (uint x) public pure returns (uint) {
require (x > 0);
uint a = 0;
uint b = 77;
while (b > a) {
uint m = a + b + 1 >> 1;
if (x >= pow10 (m)) a = m;
else b = m - 1;
}
return a;
}
function pow10 (uint x) private pure returns (uint) {
uint result = 1;
uint y = 10;
while (x > 0) {
if (x % 2 == 1) {
result *= y;
x -= 1;
} else {
y *= y;
x >>= 1;
}
}
return result;
}
Esta implementación utiliza algoritmos de bisección y exponenciación mediante el cuadrado, tiene una complejidad O(ln^2 n) y consume alrededor de 6,5K de gas. Sin embargo, parece que el enfoque O (n) sugerido por @ atomh33ls es más barato.
Pablo Razvan Berg