Skip to content

Instantly share code, notes, and snippets.

@xi
Created February 25, 2016 18:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xi/5bbe8480c48e2fc10ab5 to your computer and use it in GitHub Desktop.
Save xi/5bbe8480c48e2fc10ab5 to your computer and use it in GitHub Desktop.
Generated by SassMeister.com.
// ----
// libsass (v3.2.5)
// ----
// This provides some common mathemetical functions implemented in pure sass:
//
// - $PI
// - ln($x, $steps: 32)
// - pow($x, $exponent, $steps: 32)
// - sqrt($x, $exponent: 2, $steps: 32)
// - sin($x, $steps: 32)
// - cos($x, $steps: 32)
// - tan($x, $steps: 32)
//
// The implementations are based on taylor expansions. The `$steps` argument
// defines how many steps of the series will be calculated. So a higher number
// will result in higher precision.
//
// Taylor expansions converge quickly around their centers, so a decent
// approximation can be calculated in constant time.
//
// If the input $x is too far off of the center, it is converted to a closer
// value $y in a way that allows to calculate f($x) from f($y). This conversion
// uses exact arithmetics and can be done in constant or logarithmic time.
//
// This approach is havily inspired by
// <http://www.sassmeister.com/gist/ad6e6771df050ff3727f>. However, the
// implementations in this gist are much more efficient.
$PI: 3.141592653589793;
@function -math-exp-taylor-0($x, $steps) {
$item: 1;
$result: 1;
@for $i from 1 to $steps {
$item: $item * $x / $i;
$result: $result + $item;
}
@return $result;
}
@function -math-ln-taylor-1($x, $steps) {
$z: ($x - 1) / ($x + 1);
$power: $z;
$result: $z;
@for $i from 1 to $steps {
$power: $power * $z *$z;
$result: $result + $power / (2 * $i + 1);
}
@return 2 * $result;
}
@function -math-sin-taylor-0($x, $steps) {
$item: $x;
$result: $x;
@for $i from 1 to $steps {
$item: -$item * $x * $x / (2 * $i) / (2 * $i + 1);
$result: $result + $item;
}
@return $result;
}
@function -math-pow-int($base, $exponent) {
@if $exponent < 0 {
@return 1 / -math-pow-int($base, -$exponent);
} @else if $exponent == 0 {
@return 1;
} @else if $exponent == 1 {
@return $base;
} @else {
$exp: floor($exponent / 2);
$pow: -math-pow-int($base, $exp);
@if $exp * 2 == $exponent {
@return $pow * $pow;
} @else {
@return $pow * $pow * $base;
}
}
}
@function -math-log-approx($x) {
@if $x <= 0 {
@error "cannot calculate log of #{$x}";
} @else if $x >= 1 {
// choose the smaller option (-1) because it yield better
// results in ln().
@return str-length(inspect(round($x))) - 1;
} @else {
@return -1 * str-length(inspect(round(1 / $x)));
}
}
@function ln($x, $steps: 32) {
$ln10: 2.302585092994046;
$approx: -math-log-approx($x);
// $y is in range [1, 10]
$y: $x / -math-pow-int(10, $approx);
@return $approx * $ln10 + -math-ln-taylor-1($y, $steps);
}
@function pow($x, $exponent, $steps: 32) {
$exp1: round($exponent);
$exp2: $exponent - $exp1;
$pow1: -math-pow-int($x, $exp1);
@if $exp2 == 0 {
@return $pow1;
} @else {
$y: ln($x, $steps) * $exp2;
$pow2: -math-exp-taylor-0($y, $steps);
@return $pow1 * $pow2;
}
}
@function sqrt($x, $exponent: 2, $steps: 32) {
@return pow($x, 1 / $exponent, $steps);
}
@function sin($x, $steps: 32) {
$y: $x % (2 * $PI);
@if $y > $PI {
@return -1 * sin($y - $PI);
} @else if $y < 0 {
@return -1 * sin(-$y);
} @else {
@return -math-sin-taylor-0($y % (2 * $PI), $steps);
}
}
@function cos($x, $steps: 32) {
@return sin($x - $PI / 2, $steps);
}
@function tan($x, $steps: 32) {
@return sin($x, $steps) / $cos($x, $steps);
}
.test-ln {
input: 'ln(1)';
expected: 0;
actual: ln(1);
input: 'ln(0.1)';
expected: -2.3025850929940455;
actual: ln(0.1);
input: 'ln(123456789)';
expected: 18.63140176616802;
actual: ln(123456789);
}
.test-pow {
input: 'pow(3, 2)';
expected: 9;
actual: pow(3, 2);
input: 'pow(4, 3/2)';
expected: 8;
actual: pow(4, 3/2);
input: 'pow(144, 1/2)';
expected: 12;
actual: pow(144, 1/2);
input: 'pow(-3, 2)';
expected: 9;
actual: pow(-3, 2);
input: 'pow(3, -2)';
expected: 0.1111;
actual: pow(3, -2);
input: 'pow(64, -1/2)';
expected: 0.125;
actual: pow(64, -1/2);
input: 'pow(12, 3.4)';
expected: 4668.91789313;
actual: pow(12, 3.4);
}
.test-sin {
input: 'sin(0)';
expected: 0;
actual: sin(0);
input: 'sin($PI)';
expected: 0;
actual: sin($PI);
input: 'sin(2 * $PI)';
expected: 0;
actual: sin(2 * $PI);
input: 'sin(123456789 * $PI)';
expected: 0;
actual: sin(123456789 * $PI);
input: 'sin(-$PI)';
expected: 0;
actual: sin(-$PI);
input: 'sin(1/2 * $PI)';
expected: 1;
actual: sin(1/2 * $PI);
input: 'sin(3/2 * PI)';
expected: -1;
actual: sin(3/2 * $PI);
input: 'sin(1)';
expected: 0.8414709848078965;
actual: sin(1);
input: 'sin(2)';
expected: 0.9092974268256817;
actual: sin(2);
}
.test-ln {
input: 'ln(1)';
expected: 0;
actual: 0;
input: 'ln(0.1)';
expected: -2.3025850929940455;
actual: -2.30259;
input: 'ln(123456789)';
expected: 18.63140176616802;
actual: 18.6314;
}
.test-pow {
input: 'pow(3, 2)';
expected: 9;
actual: 9;
input: 'pow(4, 3/2)';
expected: 8;
actual: 8;
input: 'pow(144, 1/2)';
expected: 12;
actual: 12;
input: 'pow(-3, 2)';
expected: 9;
actual: 9;
input: 'pow(3, -2)';
expected: 0.1111;
actual: 0.11111;
input: 'pow(64, -1/2)';
expected: 0.125;
actual: 0.125;
input: 'pow(12, 3.4)';
expected: 4668.91789313;
actual: 4668.91789;
}
.test-sin {
input: 'sin(0)';
expected: 0;
actual: 0;
input: 'sin($PI)';
expected: 0;
actual: 0;
input: 'sin(2 * $PI)';
expected: 0;
actual: 0;
input: 'sin(123456789 * $PI)';
expected: 0;
actual: 0.0;
input: 'sin(-$PI)';
expected: 0;
actual: 0;
input: 'sin(1/2 * $PI)';
expected: 1;
actual: 1;
input: 'sin(3/2 * PI)';
expected: -1;
actual: -1;
input: 'sin(1)';
expected: 0.8414709848078965;
actual: 0.84147;
input: 'sin(2)';
expected: 0.9092974268256817;
actual: 0.9093;
}
@xi
Copy link
Author

xi commented Mar 6, 2016

This is now packaged in https://github.com/xi/sass-planifolia

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment