Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regression in Sass math computation of large numbers #807

Closed
aaronlademann-wf opened this issue Aug 28, 2019 · 4 comments
Closed

Regression in Sass math computation of large numbers #807

aaronlademann-wf opened this issue Aug 28, 2019 · 4 comments
Labels
bug Dart Issues particular to the pure Dart distribution

Comments

@aaronlademann-wf
Copy link

Hi @nex3!

We are getting ready to "flip the switch" at Workiva, forcing everyone to drop support for Dart 1.

In one of our libraries, I am seeing some odd differences in how numbers larger than 10^16 are compiled to CSS when comparing the output generated by Dart Sass version 1.6.0, and any Dart Sass version compatible with the Dart 2 SDK (>=1.10.2). I did a comparison between 1.6.0 and 1.10.2, but I didn't see anything that seemed like an obvious problem as it pertains to what I'm seeing.

In our library, we have a pow utility function that does exactly what you suspect it does:

///
/// Raises `$number` to the power of `$exponent`.
/// ---
/// @param {Number} $number
/// @param {Number} $exponent
/// ---
/// @returns {Number} - `$number` raised to the power of `$exponent`
///
@function pow($number, $exponent) {
  @if $exponent == 0 {
    @return 1;
  }

  // Initialize the value that we will return
  $value: $number;
  // If the provided number is negative, the return value should also be negative
  $preserve-negative: if($value < 0, -1, 1);

  // Multiply when `$exponent` is positive
  @if $exponent > 1 {
    @for $i from 2 through $exponent {
      $value: $value * $number;
    }

    $value: abs($value) * $preserve-negative;
  }

  // Divide when `$exponent` is negative
  @if $exponent < 1 {
    @for $i from 0 through -$exponent {
      $value: $value / $number;
    }

    $value: abs($value) * $preserve-negative;
  }

  @return $value;
}

When using Dart 1 / Dart Sass 1.6.0, the CSS output is what I expect when I call the function:

$some-var: pow(10, 16); // 10000000000000000
$some-var: pow(10, 20); // 100000000000000000000
$some-var: pow(10, 24); // 1000000000000000000000000
$some-var: pow(10, 28); // 10000000000000000000000000000
$some-var: pow(10, 32); // 100000000000000000000000000000000

However, when using Dart 2 / Dart Sass >=1.10.2, the CSS output is inconsistent when the second argument is greater than 16:

$some-var: pow(10, 16); // 10000000000000000
$some-var: pow(10, 20); // 7766279631452241920
$some-var: pow(10, 24); // 2003764205206896640
$some-var: pow(10, 28); // 4477988020393345024
$some-var: pow(10, 32); // 8814407033341083648

Note that not only do the numbers stop being accurate, but 10^24 is somehow producing a value smaller than 10^20

I've got a gross workaround implemented since in my case the first argument is always 10 and its easy enough to just append zeros to a string, but I thought I'd go ahead and report this strange behavior in case it is causing computational oddities elsewhere.

// My gross workaround when calling `pow` with a value of `10` for the first argument:
@function something-that-calls-pow($i) {
  $rate: 0;
  $exponent: 4 * (10 - $i);
  @if $exponent > 0 and $exponent <= 16 {
    $rate: pow(10, $exponent);
  } @else if $exponent > 16 {
    // In Dart 2 versions of Sass, numbers larger than 10000000000000000 are not computed / rendered as expected.
    // For instance, `pow(10, 20) = 7766279631452241920` and `pow(10, 24) = 2003764205206896640`
    // with 10^24 somehow producing a smaller number than 10^20
    $rate: '10000000000000000';
    $number-of-extra-zeros-to-add: $exponent - 16;
    @while $number-of-extra-zeros-to-add > 0 {
      $rate: '#{$rate}0';
      $number-of-extra-zeros-to-add: $number-of-extra-zeros-to-add - 1;
    }
  }

  @return #{$rate};
}
@nex3
Copy link
Contributor

nex3 commented Aug 29, 2019

I'm guessing this is because Dart 2.0.0 dropped automatic bignum conversion for efficiency reasons. I don't know if we want to rely on bignum support for the same reason that Dart moved away from it, but we should probably move to floating-point arithmetic at some point (which I believe is what LibSass does). I'll bring it up at the next Sass design meeting.

As a less-gross workaround for now, you can force floating-point arithmetic by adding .0 to the end of the literals.

@nex3 nex3 added bug Dart Issues particular to the pure Dart distribution labels Aug 29, 2019
@nex3
Copy link
Contributor

nex3 commented Sep 10, 2019

After discussing this with the Sass team, we're leaning towards using floating-point arithmetic everywhere, but we'd like to hear more about your specific use case. How did you end up calling pow() with such large numbers?

@nex3
Copy link
Contributor

nex3 commented Jan 7, 2020

@aaronlademann-wf Can you give us some context here?

@nex3
Copy link
Contributor

nex3 commented Jun 15, 2022

Tracking this in sass/sass#2892.

@nex3 nex3 closed this as not planned Won't fix, can't repro, duplicate, stale Jun 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Dart Issues particular to the pure Dart distribution
Projects
None yet
Development

No branches or pull requests

2 participants