sass / sass

Sass makes CSS fun!
https://sass-lang.com
MIT License
15.1k stars 2.16k forks source link

Fuzzy equality requires precision of 11 while dart-sass only has precision of 10 #3953

Open ntkme opened 3 weeks ago

ntkme commented 3 weeks ago

https://github.com/sass/sass/blob/main/spec/types/number.md#fuzzy-equality

They are both finite numbers and the mathematical numbers they represent produce the same value when rounded to the nearest 1e⁻¹¹ (with ties away from zero).

In my understanding, "precision of 10" means two internal numbers are considered equal if they are equal after rounding them up to 1e⁻¹⁰, . In other words, with dart-sass being precision of 10, if two SassNumbers serialize to the same String after rounding, they should be equal, however, I discovered that fuzzy equality requires a precision of 11 in spec instead of precision of 10, which leads to a very confusing situation.

https://sass-lang.com/playground/#eJwzNFRJtFIw0DMwNDI2MTUzt7C05lJJQhUxMQWKJcaX5McXWykoV6sk1oLUIPhJQD5XSWpxiUI1l4JCamFpYk5mSaVuYrFucUlRZl66lQJUt4KtrQJUozWayrzS3KTUIpBKiCJrrloAgV8sUQ==

> a = SassNumber(0.0123456789)
0.0123456789
> b = SassNumber(0.012345678945)
0.0123456789
> a.toString() == b.toString()
true
> a.equals(b)
false
ntkme commented 3 weeks ago

I guess this spec comes from ruby-sass historically had a implementation that did not compare the rounding of two numbers: https://github.com/sass/ruby-sass/blob/stable/lib/sass/script/value/number.rb#L408-L410

    def self.basically_equal?(num1, num2)
      (num1 - num2).abs < epsilon
    end

In the original ruby implementation the epsilon is 1e⁻¹¹, likely because the equality check does not do any rounding, thus it need one extra digit to tell the difference between 0.01234567894 (rounds to 0.0123456789) and 0.01234567895 (rounds to 0.0123456790). Meanwhile, because it does not have any rounding considered, 0.012345678945 (rounds to 0.0123456789) would end up being fuzzy equal to 0.01234567895 (rounds to 0.0123456790), which is also confusing.

nex3 commented 3 weeks ago

It is more or less intentional that the equality comparison uses more precision than the serialized values. I think you may be right that this was initially a workaround for the kinda-broken pre-#2892 equality semantics, but at this point I don't think it's worth the pain of changing it. Reducing the precision for equality checks is a backwards-incompatible change that's likely to cause subtle hard-to-find issues, which is especially hard to stomach given how many deprecations we've done recently. Increasing the precision of string outputs is probably more feasible, but it really produces no user value beyond specifically debugging "why aren't these two numbers that looks the same not equal"—I don't think any browser will actually use the eleventh digit of precision, and users do care about payload size.

I guess we could find some sort of middle ground by producing an extra digit of precision specifically for inspect mode, so at least @debug produced more information.