diff --git a/modules/juce_gui_basics/layout/juce_Grid.cpp b/modules/juce_gui_basics/layout/juce_Grid.cpp
index 4ef0454ca5..4c92ff6027 100644
--- a/modules/juce_gui_basics/layout/juce_Grid.cpp
+++ b/modules/juce_gui_basics/layout/juce_Grid.cpp
@@ -39,15 +39,22 @@ struct Tracks
 
 struct Grid::SizeCalculation
 {
-    static float getTotalAbsoluteSize (const Array<TrackInfo>& tracks, Px gapSize) noexcept
+    SizeCalculation() = default;
+
+    SizeCalculation (std::function<float (float)> roundingFunctionIn)
+        : roundingFunction (std::move (roundingFunctionIn))
+    {
+    }
+
+    float getTotalAbsoluteSize (const Array<TrackInfo>& tracks, Px gapSize) noexcept
     {
         float totalCellSize = 0.0f;
 
         for (const auto& trackInfo : tracks)
             if (! trackInfo.isFractional() || trackInfo.isAuto())
-                totalCellSize += trackInfo.getSize();
+                totalCellSize += roundingFunction (trackInfo.getSize());
 
-        float totalGap = tracks.size() > 1 ? static_cast<float> ((tracks.size() - 1) * gapSize.pixels)
+        float totalGap = tracks.size() > 1 ? (float) (tracks.size() - 1) * roundingFunction ((float) gapSize.pixels)
                                            : 0.0f;
 
         return totalCellSize + totalGap;
@@ -67,24 +74,24 @@ struct Grid::SizeCalculation
     }
 
     //==============================================================================
-    static float getTotalAbsoluteHeight (const Array<TrackInfo>& rowTracks, Px rowGap)
+    float getTotalAbsoluteHeight (const Array<TrackInfo>& rowTracks, Px rowGapSize)
     {
-        return getTotalAbsoluteSize (rowTracks, rowGap);
+        return getTotalAbsoluteSize (rowTracks, rowGapSize);
     }
 
-    static float getTotalAbsoluteWidth (const Array<TrackInfo>& columnTracks, Px columnGap)
+    float getTotalAbsoluteWidth (const Array<TrackInfo>& columnTracks, Px columnGapSize)
     {
-        return getTotalAbsoluteSize (columnTracks, columnGap);
+        return getTotalAbsoluteSize (columnTracks, columnGapSize);
     }
 
-    static float getRelativeWidthUnit (float gridWidth, Px columnGap, const Array<TrackInfo>& columnTracks)
+    float getRelativeWidthUnit (float gridWidth, Px columnGapSize, const Array<TrackInfo>& columnTracks)
     {
-        return getRelativeUnitSize (gridWidth, getTotalAbsoluteWidth (columnTracks, columnGap), columnTracks);
+        return getRelativeUnitSize (gridWidth, getTotalAbsoluteWidth (columnTracks, columnGapSize), columnTracks);
     }
 
-    static float getRelativeHeightUnit (float gridHeight, Px rowGap, const Array<TrackInfo>& rowTracks)
+    float getRelativeHeightUnit (float gridHeight, Px rowGapSize, const Array<TrackInfo>& rowTracks)
     {
-        return getRelativeUnitSize (gridHeight, getTotalAbsoluteHeight (rowTracks, rowGap), rowTracks);
+        return getRelativeUnitSize (gridHeight, getTotalAbsoluteHeight (rowTracks, rowGapSize), rowTracks);
     }
 
     //==============================================================================
@@ -100,20 +107,94 @@ struct Grid::SizeCalculation
                        const Tracks& tracks)
     {
         if (hasAnyFractions (tracks.columns.items))
+        {
             relativeWidthUnit = getRelativeWidthUnit (gridWidth, columnGapToUse, tracks.columns.items);
+            fractionallyDividedWidth = gridWidth - getTotalAbsoluteSize (tracks.columns.items, columnGapToUse);
+        }
         else
+        {
             remainingWidth = gridWidth - getTotalAbsoluteSize (tracks.columns.items, columnGapToUse);
+        }
 
         if (hasAnyFractions (tracks.rows.items))
+        {
             relativeHeightUnit = getRelativeHeightUnit (gridHeight, rowGapToUse, tracks.rows.items);
+            fractionallyDividedHeight = gridHeight - getTotalAbsoluteSize (tracks.rows.items, rowGapToUse);
+        }
         else
+        {
             remainingHeight = gridHeight - getTotalAbsoluteSize (tracks.rows.items, rowGapToUse);
+        }
+
+        const auto calculateTrackBounds = [&] (auto& outBounds,
+                                               const auto& trackItems,
+                                               auto relativeUnit,
+                                               auto totalSizeForFractionalItems,
+                                               auto gap)
+        {
+            const auto lastFractionalIndex = [&]
+            {
+                for (int i = trackItems.size() - 1; 0 <= i; --i)
+                    if (trackItems[i].isFractional())
+                        return i;
+
+                return -1;
+            }();
+
+            float start = 0.0f;
+            float carriedError = 0.0f;
+
+            for (int i = 0; i < trackItems.size(); ++i)
+            {
+                const auto& currentItem = trackItems[i];
+
+                const auto currentTrackSize = [&]
+                {
+                    if (i == lastFractionalIndex)
+                        return totalSizeForFractionalItems;
+
+                    const auto absoluteSize = currentItem.getAbsoluteSize (relativeUnit);
+
+                    if (! currentItem.isFractional())
+                        return roundingFunction (absoluteSize);
+
+                    const auto result = roundingFunction (absoluteSize + carriedError);
+                    carriedError = result - absoluteSize;
+                    return result;
+                }();
+
+                if (currentItem.isFractional())
+                    totalSizeForFractionalItems -= currentTrackSize;
+
+                const auto end = start + currentTrackSize;
+                outBounds.emplace_back (start, end);
+                start = end + roundingFunction (static_cast<float> (gap.pixels));
+            }
+        };
+
+        calculateTrackBounds (columnTrackBounds,
+                              tracks.columns.items,
+                              relativeWidthUnit,
+                              fractionallyDividedWidth,
+                              columnGapToUse);
+
+        calculateTrackBounds (rowTrackBounds,
+                              tracks.rows.items,
+                              relativeHeightUnit,
+                              fractionallyDividedHeight,
+                              rowGapToUse);
     }
 
-    float relativeWidthUnit  = 0.0f;
-    float relativeHeightUnit = 0.0f;
-    float remainingWidth     = 0.0f;
-    float remainingHeight    = 0.0f;
+    float relativeWidthUnit         = 0.0f;
+    float relativeHeightUnit        = 0.0f;
+    float fractionallyDividedWidth  = 0.0f;
+    float fractionallyDividedHeight = 0.0f;
+    float remainingWidth            = 0.0f;
+    float remainingHeight           = 0.0f;
+
+    std::vector<Range<float>> columnTrackBounds;
+    std::vector<Range<float>> rowTrackBounds;
+    std::function<float (float)> roundingFunction = [] (auto v) { return v; };
 };
 
 //==============================================================================
@@ -405,20 +486,9 @@ struct Grid::PlacementHelpers
     }
 
     //==============================================================================
-    static float getCoord (int trackNumber, float relativeUnit, Px gap, const Array<TrackInfo>& tracks)
-    {
-        float c = 0;
-
-        for (const auto* it = tracks.begin(); it != tracks.begin() + trackNumber; ++it)
-            c += it->getAbsoluteSize (relativeUnit) + static_cast<float> (gap.pixels);
-
-        return c;
-    }
-
     static Rectangle<float> getCellBounds (int columnNumber, int rowNumber,
                                            const Tracks& tracks,
-                                           SizeCalculation calculation,
-                                           Px columnGap, Px rowGap)
+                                           SizeCalculation calculation)
     {
         const auto correctedColumn = columnNumber - 1 + tracks.columns.numImplicitLeading;
         const auto correctedRow    = rowNumber    - 1 + tracks.rows   .numImplicitLeading;
@@ -426,10 +496,13 @@ struct Grid::PlacementHelpers
         jassert (isPositiveAndBelow (correctedColumn, tracks.columns.items.size()));
         jassert (isPositiveAndBelow (correctedRow,    tracks.rows   .items.size()));
 
-        return { getCoord (correctedColumn, calculation.relativeWidthUnit,  columnGap, tracks.columns.items),
-                 getCoord (correctedRow,    calculation.relativeHeightUnit, rowGap,    tracks.rows   .items),
-                 tracks.columns.items.getReference (correctedColumn).getAbsoluteSize (calculation.relativeWidthUnit),
-                 tracks.rows   .items.getReference (correctedRow)   .getAbsoluteSize (calculation.relativeHeightUnit) };
+        return
+        {
+            calculation.columnTrackBounds[(size_t) correctedColumn].getStart(),
+            calculation.rowTrackBounds[(size_t) correctedRow].getStart(),
+            calculation.columnTrackBounds[(size_t) correctedColumn].getEnd() - calculation.columnTrackBounds[(size_t) correctedColumn].getStart(),
+            calculation.rowTrackBounds[(size_t) correctedRow].getEnd() - calculation.rowTrackBounds[(size_t) correctedRow].getStart()
+        };
     }
 
     static Rectangle<float> alignCell (Rectangle<float> area,
@@ -501,12 +574,11 @@ struct Grid::PlacementHelpers
                                            const Tracks& tracks,
                                            SizeCalculation calculation,
                                            AlignContent alignContent,
-                                           JustifyContent justifyContent,
-                                           Px columnGap, Px rowGap)
+                                           JustifyContent justifyContent)
     {
         const auto findAlignedCell = [&] (int column, int row)
         {
-            const auto cell = getCellBounds (column, row, tracks, calculation, columnGap, rowGap);
+            const auto cell = getCellBounds (column, row, tracks, calculation);
             return alignCell (cell,
                               column,
                               row,
@@ -927,7 +999,8 @@ struct Grid::BoxAlignment
 {
     static Rectangle<float> alignItem (const GridItem& item,
                                        const Grid& grid,
-                                       Rectangle<float> area)
+                                       Rectangle<float> area,
+                                       const std::function<float (float)>& roundingFunction)
     {
         // if item align is auto, inherit value from grid
         const auto alignType = item.alignSelf == GridItem::AlignSelf::autoValue
@@ -945,6 +1018,14 @@ struct Grid::BoxAlignment
         // align and justify
         auto r = area;
 
+        const auto rounded = [&] (auto a) -> decltype (a)
+        {
+            return { roundingFunction (a.getX()),
+                     roundingFunction (a.getY()),
+                     roundingFunction (a.getWidth()),
+                     roundingFunction (a.getHeight()) };
+        };
+
         if (! approximatelyEqual (item.width,     (float) GridItem::notAssigned))  r.setWidth  (item.width);
         if (! approximatelyEqual (item.height,    (float) GridItem::notAssigned))  r.setHeight (item.height);
         if (! approximatelyEqual (item.maxWidth,  (float) GridItem::notAssigned))  r.setWidth  (jmin (item.maxWidth,  r.getWidth()));
@@ -953,14 +1034,14 @@ struct Grid::BoxAlignment
         if (item.minHeight > 0.0f)                            r.setHeight (jmax (item.minHeight, r.getHeight()));
 
         if (alignType == AlignItems::start && justifyType == JustifyItems::start)
-            return r;
+            return rounded (r);
 
         if (alignType   == AlignItems::end)       r.setY (r.getY() + (area.getHeight() - r.getHeight()));
         if (justifyType == JustifyItems::end)     r.setX (r.getX() + (area.getWidth()  - r.getWidth()));
         if (alignType   == AlignItems::center)    r.setCentre (r.getCentreX(),    area.getCentreY());
         if (justifyType == JustifyItems::center)  r.setCentre (area.getCentreX(), r.getCentreY());
 
-        return r;
+        return rounded (r);
     }
 };
 
@@ -1024,30 +1105,41 @@ void Grid::performLayout (Rectangle<int> targetArea)
     AutoPlacement::applySizeForAutoTracks (implicitTracks, itemsAndAreas);
 
     SizeCalculation calculation;
-    calculation.computeSizes (targetArea.toFloat().getWidth(),
-                              targetArea.toFloat().getHeight(),
-                              columnGap,
-                              rowGap,
-                              implicitTracks);
+    SizeCalculation roundedCalculation { [] (auto v) { return std::round (v); } };
+
+    for (auto* c : { &calculation, &roundedCalculation })
+        c->computeSizes (targetArea.toFloat().getWidth(),
+                         targetArea.toFloat().getHeight(),
+                         columnGap,
+                         rowGap,
+                         implicitTracks);
 
     for (auto& itemAndArea : itemsAndAreas)
     {
-        const auto a = itemAndArea.second;
-        const auto areaBounds = PlacementHelpers::getAreaBounds (a.column,
-                                                                 a.row,
-                                                                 implicitTracks,
-                                                                 calculation,
-                                                                 alignContent,
-                                                                 justifyContent,
-                                                                 columnGap,
-                                                                 rowGap);
-
         auto* item = itemAndArea.first;
-        item->currentBounds = BoxAlignment::alignItem (*item, *this, areaBounds)
-                                + targetArea.toFloat().getPosition();
+
+        const auto getBounds = [&] (auto& sizeCalculation)
+        {
+            const auto a = itemAndArea.second;
+
+            const auto areaBounds = PlacementHelpers::getAreaBounds (a.column,
+                                                                     a.row,
+                                                                     implicitTracks,
+                                                                     sizeCalculation,
+                                                                     alignContent,
+                                                                     justifyContent);
+
+            return BoxAlignment::alignItem (*item,
+                                            *this,
+                                            areaBounds,
+                                            sizeCalculation.roundingFunction)
+                       + targetArea.toFloat().getPosition();
+        };
+
+        item->currentBounds = getBounds (calculation) + targetArea.toFloat().getPosition();
 
         if (auto* c = item->associatedComponent)
-            c->setBounds (item->currentBounds.getSmallestIntegerContainer());
+            c->setBounds (getBounds (roundedCalculation).toNearestIntEdges() + targetArea.getPosition());
     }
 }
 
