Power Function

Avatar of Kitty Giraudel
Kitty Giraudel on

While very helpful with arithmetic, Sass falls a bit short with mathematical helper functions. There has been an open issue on the official repository to ask for more math-related functions for almost 3 years.

Some third-party vendors like Compass or SassyMath provide advanced support for math features, but they are external dependencies which could (should?) be avoided.

One of the most popular request on this matter is a power function or even a exponent operator. Unfortunately, there is still no support for this in Sass and while it is still under active discussion, it is unlikely to happen very soon.

Meanwhile, being able to raise a number to a certain power happens to be very useful in CSS. Here are a few examples where it would come in handy:

Sass implementation

Fortunately for us, it is possible (and quite simple) to create a power function with nothing but Sass. All we need is a loop and some basic mathematical operators (such as * and /).

Positive integer exponents

Our function (named pow) in its smallest form would look like this:

@function pow($number, $exponent) {
  $value: 1;

  @if $exponent > 0 {
    @for $i from 1 through $exponent {
      $value: $value * $number;
    }
  }

  @return $value;
}

Here is an example:

.foo {
  width: pow(20, 2) * 1px; // 400px
}

Positive or negative integer exponents

However it only works with a positive `$power` argument. Allowing negative exponents wouldn’t be so hard, we just need a small extra condition:

@function pow($number, $exponent) {
  $value: 1;

  @if $exponent > 0 {
    @for $i from 1 through $exponent {
      $value: $value * $number;
    }
  } @else if $exponent < 0 {
    @for $i from 1 through -$exponent {
      $value: $value / $number;
    }
  }

  @return $value;
}

Here is an example:

.foo {
  width: pow(10, -2) * 1px; // 0.0001px
}

Positive or negative exponents

Now, what if we want non-integer exponents? Such as 4.2 for instance? The truth is that it really is not easy. It still is doable, but it requires more than just a loop and a few operations.

This has been done on the Bourbon repository in order to complete the modular-scale(..) function from the framework (although has been declined). Here is the code:

@function pow($number, $exponent) {
  @if (round($exponent) != $exponent) {
    @return exp($exponent * ln($number));
  }
    
  $value: 1;
  
  @if $exponent > 0 {
    @for $i from 1 through $exponent {
     $value: $value * $number;
    }
  } @else if $exponent < 0 {
    @for $i from 1 through -$exponent {
      $value: $value / $number;
    }
  }

  @return $value;
}

@function factorial($value) {
  $result: 1;

  @if $value == 0 {
    @return $result;
  }
  
  @for $index from 1 through $value {
    $result: $result * $index;
  }
  
  @return $result;
}

@function summation($iteratee, $input, $initial: 0, $limit: 100) {
  $sum: 0;
  
  @for $index from $initial to $limit {
    $sum: $sum + call($iteratee, $input, $index);
  }
  
  @return $sum;
}

@function exp-maclaurin($x, $n) {
  @return (pow($x, $n) / factorial($n));
}

@function exp($value) {
  @return summation('exp-maclaurin', $value, 0, 100);
}

@function ln-maclaurin($x, $n) {
  @return (pow(-1, $n + 1) / $n) * (pow($x - 1, $n));
}

@function ln($value) {
  $ten-exp: 1;
  $ln-ten: 2.30258509;
  
  @while ($value > pow(10, $ten-exp)) {
    $ten-exp: $ten-exp + 1;
  }
  
  @return summation(ln-maclaurin, $value / pow(10, $ten-exp), 1, 100) + $ten-exp * $ln-ten;
}

Further considerations

Well, that was intense. If you happen to need support for non-integer exponents (like 4.2), I recommend you use an external dependency which provides it (such as sass-math-pow) instead of including all this code in your project. Not that it is a bad thing per se, but it is not really the role of your project to host such a large a mount of non-authored polyfilling code (that’s why we have package managers).

Also note that all these operations are quite intensive for such a thin layer as Sass. At this point, and if your design rely on advanced mathematical functions, it is probably better to pass the implementation of these helpers from the upper layer (Node.js, Ruby, etc.) down to Sass through a plugin system (Eyeglass, Ruby gem, etc.).

But for basic pow(..) usage, I can’t recommend the simple versions enough!