3

What is a way of getting the order of magnitude of an integer (or float) in a solidity contract?

e.g. such that

MyContract.getOrderMag(2564)   returns 3
MyContract.getOrderMag(987)   returns 2
MyContract.getOrderMag(2.3e76)   returns 76

Ideally in the least gas using way possible...

Here's what I have:

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;
    }
}

This doesn't work with negative numbers or, as pointed out by @RichardHorrocks, when input is 10.

2

Just thinking about this a bit more...

Your code takes a (signed) int256 as input. The maximum (absolute) value of this is around 1e+76, which would equate to 76 cycles of the while loop, each composed of multiple instructions.

In such cases it would probably be cheaper to use a logarithmic method - i.e. take the log10 of the input and ignore any fractional parts. (I say "probably" - I haven't checked.)

It will depend on the what values you expect input to usually take, and how quickly the logarithm algorithm converges on an answer.

  • Edit: I'd said "unsigned" instead of "signed"... – Richard Horrocks Jan 11 '17 at 16:35
1

Here's how I did it. There is probably significant scope for improvement. I would accept any answer that uses less 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;
}
}
  • 1
    As it currently stands, if input is 10, then counter is 0 instead of 1 (because of the while condition). – Richard Horrocks Jan 11 '17 at 10:57
  • 1
    You could just remove the temp variable and use input in the calculation. – Richard Horrocks Jan 11 '17 at 10:58
  • @RichardHorrocks Thanks. Yes it's a bit broken; also doesn't work with negative numbers. Going to delete answer an post in edited question. – atomh33ls Jan 11 '17 at 11:04
  • @RichardHorrocks attempted to fix – atomh33ls Jan 11 '17 at 11:21
  • Looks good :) I've added another answer that considers the size of the input - it may or may not be relevant, depending on how big you think the input will normally be. – Richard Horrocks Jan 11 '17 at 14:03
1
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;
}

This implementation uses bisection and exponentiation by squaring algorithms, has O(ln^2 n) complexity and consumes about 6.5K gas. Though, it seems that O(n) approach as suggested by @atomh33ls is cheaper.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.