Precision loss in CSS transforms due to use of 32-bit floats

UNCONFIRMED
Unassigned

Status

()

Core
Layout
UNCONFIRMED
2 years ago
2 years ago

People

(Reporter: ivan, Unassigned)

Tracking

38 Branch
Points:
---

Firefox Tracking Flags

(Not tracked)

Details

Attachments

(1 attachment)

(Reporter)

Description

2 years ago
User Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.1.0
Build ID: 20150711212448

Steps to reproduce:

Hi, LeafletJS dev here. We've been having a problem with our library (https://github.com/Leaflet/Leaflet/issues/3608) that we've tracked down to Firefox's management of pixel lengths inside CSS transforms.

Apparently Firefox is rounding big numbers inside CSS transforms. This is best explained with the following bit of Javascript:

  <script>
    var foo = document.createElement('div');
    foo.style.transform = "translate3d(-11374952px, 21881153px, 0)";
    console.log(foo.style.transform);
    // Expected: "translate3d(-11374952px, 21881153px, 0)";
    // Actual: "translate3d(-1.1375e+7px, 2.18812e+7px, 0px)"
  </script>

Other browsers (e.g. Chrome) behave as expected, without losing precision. Scientific notation seems to trigger exactly at 10^7px.

This is similar to #470769, but applies to CSS lenghts inside transforms instead.
(Reporter)

Comment 1

2 years ago
See also https://jsfiddle.net/dg6r5hhb/ - two opposite transforms should always negate each other perfectly.

Comment 2

2 years ago
Created attachment 8659830 [details]
1203873.html (testcase)

Updated

2 years ago
Component: Untriaged → Layout
Product: Firefox → Core
The transform is stored internally (and more importantly, represented by the graphics library) as a floating point number.  A 32-bit float has only 7 digits of decimal precision, yes...  The issue is not the scientific notation; that's just a serialization artefact.

Simple testcase showing the rounding during rendering, since again the graphics library works with 32-bit floats:

  <!DOCTYPE html>
  <style>
    div { width: 100px; height: 100px; }
    body { padding: 0; margin: 0; }
  </style>
  <div style="position: absolute; top: 11374952px; background: red"></div>
  <div style="transform: translate(0, 11374952px); background: green"></div>

(note the thin stripe of red at the bottom).

Or is your library actually examining the serialized values and acting on those so you're losing more precision than that testcase shows?
Flags: needinfo?(ivan)
Summary: Precision loss in CSS transforms due to automatic scientific notation → Precision loss in CSS transforms due to use of 32-bit floats
(Reporter)

Comment 4

2 years ago
(In reply to Boris Zbarsky [:bz] from comment #3)
> Or is your library actually examining the serialized values and acting on
> those so you're losing more precision than that testcase shows?

You can see our original issue here:
https://github.com/Leaflet/Leaflet/issues/3608

We use CSS transforms to pan the map around (move to another point on the earth without changing zoom level). If the pan is too big, the map turns into a grey <div>. Right now we've patched around the issue by resetting the state if a transform is bigger than 10^7px.
Flags: needinfo?(ivan)
I still can't tell from that whether the issue is reading back the transform values and working with those numbers or whether the issue is just the transform being set (but not read) and then rendering incorrectly...
(Reporter)

Comment 6

2 years ago
(In reply to Boris Zbarsky [:bz] from comment #5)
> I still can't tell from that whether the issue is reading back the transform
> values and working with those numbers or whether the issue is just the
> transform being set (but not read) and then rendering incorrectly...

The problem is incorrect rendering. Reading back different values is just a symptom.
You need to log in before you can comment on or make changes to this bug.