Layout bug caused by floating point rounding error #749

Open
opened 2018-04-10 05:35:25 -07:00 by leafduo · 10 comments
leafduo commented 2018-04-10 05:35:25 -07:00 (Migrated from github.com)

Report

Issues and Steps to Reproduce

  • Layout multiple layers of views, and put a label as leaf node.
  • Run it in a 3x resolution screen, like iPhone X or one of the Plus models.

Expected Behavior

The text on the label should be displayed without problem

Actual Behavior

The text got truncated.
screen shot 2018-04-08 at 20 30 26

Link to Code

Example project to reproduce the issue https://github.com/leafduo/yoga-rounding

image

The problem was caused by the accumulation of floating point rounding error in complex layout. The view tree in the example has a height of 4, and causes floating point rounding errors to accumulate. In YGRoundValueToPixelGrid, the fractialvariable is 0.999877929 and is considered not a whole integer. In the end, the width of the label is one pixel short, causing the text truncation.

This issue is presented when pointScaleFactor is 3 (not 1 or 2), and perhaps it's because 1) 3 is bigger and will cause more error, and 2) multiply and division by 2 is pretty accurate because the binary nature of computer.

Using double instead of float to represent points, or raising the threshold in YGFloatsEqual will make the text fully displayed in the example, but not solve this problem completely. It will still exist in a more complex layout.

I think maybe we can store scaled value or use a real fraction representation?

# Report - [x] I have searched [existing issues](https://github.com/facebook/yoga/issues) and this is not a duplicate # Issues and Steps to Reproduce - Layout multiple layers of views, and put a label as leaf node. - Run it in a 3x resolution screen, like iPhone X or one of the Plus models. # Expected Behavior The text on the label should be displayed without problem # Actual Behavior The text got truncated. ![screen shot 2018-04-08 at 20 30 26](https://user-images.githubusercontent.com/28619/38467488-b7ab5598-3b6b-11e8-9455-369e9ab9ec36.png) # Link to Code Example project to reproduce the issue https://github.com/leafduo/yoga-rounding ![image](https://user-images.githubusercontent.com/28619/38539557-44fe3f86-3ccb-11e8-85a9-eca99b6cfd66.png) The problem was caused by the accumulation of floating point rounding error in complex layout. The view tree in the example has a height of 4, and causes floating point rounding errors to accumulate. In `YGRoundValueToPixelGrid`, the `fractial`variable is 0.999877929 and is considered not a whole integer. In the end, the width of the label is one pixel short, causing the text truncation. This issue is presented when `pointScaleFactor` is 3 (not 1 or 2), and perhaps it's because 1) 3 is bigger and will cause more error, and 2) multiply and division by 2 is pretty accurate because the binary nature of computer. Using `double` instead of `float` to represent points, or raising the threshold in `YGFloatsEqual` will make the text fully displayed in the example, but not solve this problem completely. It will still exist in a more complex layout. I think maybe we can store scaled value or use a real fraction representation?
LeoSchleicher commented 2018-04-10 06:11:26 -07:00 (Migrated from github.com)

I think this problem is the same as mine:
https://github.com/facebook/yoga/issues/738

I think this problem is the same as mine: https://github.com/facebook/yoga/issues/738
linqingmo commented 2018-04-10 07:30:09 -07:00 (Migrated from github.com)

how about YGConfigSetPointScaleFactor(globalConfig, 0);?

how about `YGConfigSetPointScaleFactor(globalConfig, 0);`?
leafduo commented 2018-04-10 23:26:20 -07:00 (Migrated from github.com)

@LeoSchleicher It appears so.

@LeoSchleicher It appears so.
leafduo commented 2018-04-10 23:29:03 -07:00 (Migrated from github.com)

@linqingmo I've tried to set pointScaleFactor to 0, and the truncation will be solved. But will it cause view not align to pixel grid and become blurry?

@linqingmo I've tried to set `pointScaleFactor` to 0, and the truncation will be solved. But will it cause view not align to pixel grid and become blurry?
jmaurice-unity commented 2018-04-11 11:50:20 -07:00 (Migrated from github.com)

You might want to try with these changes : https://github.com/facebook/yoga/pull/688

You might want to try with these changes : https://github.com/facebook/yoga/pull/688
leafduo commented 2018-04-11 20:51:03 -07:00 (Migrated from github.com)

@jmaurice-unity It's not related, #688 is about bug when rounding negative numbers, my coordinates are all positive.

@jmaurice-unity It's not related, #688 is about bug when rounding negative numbers, my coordinates are all positive.
LeoSchleicher commented 2018-05-03 04:23:35 -07:00 (Migrated from github.com)

Can I set YGConfigSetPointScaleFactor outside of Yoga sources? How to get globalConfig?

Can I set YGConfigSetPointScaleFactor outside of Yoga sources? How to get globalConfig?
lunarraid commented 2018-12-05 14:19:08 -08:00 (Migrated from github.com)

I believe I am running into this issue as well. If I set the pointScaleFactor to 0, then widths are calculated correctly. If not, continuous updates lead to constant shrinking of elements. I have an example of this running here: https://codesandbox.io/s/qxnnm7272j

I believe I am running into this issue as well. If I set the `pointScaleFactor` to 0, then widths are calculated correctly. If not, continuous updates lead to constant shrinking of elements. I have an example of this running here: https://codesandbox.io/s/qxnnm7272j
ahuinee commented 2020-05-12 19:38:51 -07:00 (Migrated from github.com)

我尝试了一下,字符串后面加一个空格可以解决 😅
I tried it, and add a space after string. hhhh

我尝试了一下,字符串后面加一个空格可以解决 😅 I tried it, and add a space after string. hhhh
matthieugicquel commented 2025-08-26 02:58:49 -07:00 (Migrated from github.com)

I'm frequently seeing this (or a similar?) issue on text nodes with lots of siblings. Node height gets rounded down, and iOS makes the text overflow on the penultimate line instead of wrapping:

Image

A failing test for the issue could look like this, if we consider that roundLayoutResultsToPixelGrid should accept any float dimensions and still never round down text nodes / nodes with custom measure functions:

TEST(YogaTest, roundLayoutResultsToPixelGrid_height_rounding_bug) {
  YGConfigRef config = YGConfigNew();
  YGConfigSetPointScaleFactor(config, 3);

  YGNodeRef node = YGNodeNewWithConfig(config);
  YGNodeStyleSetPositionType(node, YGPositionTypeAbsolute);

  // These are values extracted from a debugging session in a real iOS app
  // Question: Are these realistic input values, or is the problem "upstream", with these position values never supposed to be set on a node?
  YGNodeStyleSetPosition(node, YGEdgeLeft, 38.333333969116211);
  YGNodeStyleSetPosition(node, YGEdgeTop, 1970.3333333432674);
  YGNodeStyleSetWidth(node, 339.66665649414063);
  YGNodeStyleSetHeight(node, 96);
  YGNodeSetNodeType(node, YGNodeTypeText);

  YGNodeCalculateLayout(node, YGUndefined, YGUndefined, YGDirectionLTR);

  // This should be 96 but it gets rounded 95.9999988
  // -> iOS refuses to render 4 lines of 24px text and the last line overflows/doesn't wrap
  ASSERT_FLOAT_EQ(YGNodeLayoutGetHeight(node), 96.0f);

  YGNodeFreeRecursive(node);

  YGConfigFree(config);
}

I'm using this patch on my app. but there's also this existing PR. I don't understand the algorithm well enough to know what the right fix should be...

Similar or adjacent issue: https://github.com/facebook/yoga/issues/1574

I'm frequently seeing this (or a similar?) issue on text nodes with lots of siblings. Node height gets rounded down, and iOS makes the text overflow on the penultimate line instead of wrapping: <img width="300" alt="Image" src="https://github.com/user-attachments/assets/9f93908b-7068-49eb-aae2-759b55294ced" /> A failing test for the issue could look like this, if we consider that `roundLayoutResultsToPixelGrid` should accept any float dimensions and still never round down text nodes / nodes with custom measure functions: ```cpp TEST(YogaTest, roundLayoutResultsToPixelGrid_height_rounding_bug) { YGConfigRef config = YGConfigNew(); YGConfigSetPointScaleFactor(config, 3); YGNodeRef node = YGNodeNewWithConfig(config); YGNodeStyleSetPositionType(node, YGPositionTypeAbsolute); // These are values extracted from a debugging session in a real iOS app // Question: Are these realistic input values, or is the problem "upstream", with these position values never supposed to be set on a node? YGNodeStyleSetPosition(node, YGEdgeLeft, 38.333333969116211); YGNodeStyleSetPosition(node, YGEdgeTop, 1970.3333333432674); YGNodeStyleSetWidth(node, 339.66665649414063); YGNodeStyleSetHeight(node, 96); YGNodeSetNodeType(node, YGNodeTypeText); YGNodeCalculateLayout(node, YGUndefined, YGUndefined, YGDirectionLTR); // This should be 96 but it gets rounded 95.9999988 // -> iOS refuses to render 4 lines of 24px text and the last line overflows/doesn't wrap ASSERT_FLOAT_EQ(YGNodeLayoutGetHeight(node), 96.0f); YGNodeFreeRecursive(node); YGConfigFree(config); } ``` I'm [using this patch on my app.](https://github.com/matthieugicquel/yoga/commit/123cd44f5a9904391aea580a64753143a7d17e42) but there's also [this existing PR](https://github.com/facebook/yoga/pull/902/commits/19035c90e9107226cc53d91f6722ee331339624a). I don't understand the algorithm well enough to know what the right fix should be... Similar or adjacent issue: https://github.com/facebook/yoga/issues/1574
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DaddyFrosty/yoga#749
No description provided.