diff --git a/common/geometry/shape_poly_set.cpp b/common/geometry/shape_poly_set.cpp
index 7d6cc74f1a..194d307ba7 100644
--- a/common/geometry/shape_poly_set.cpp
+++ b/common/geometry/shape_poly_set.cpp
@@ -38,6 +38,7 @@
 #include <md5_hash.h>
 #include <map>
 
+#include <geometry/geometry_utils.h>
 #include <geometry/shape.h>
 #include <geometry/shape_line_chain.h>
 #include <geometry/shape_poly_set.h>
@@ -1575,10 +1576,10 @@ SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::ChamferPolygon( unsigned int aDistance,
 
 
 SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::FilletPolygon( unsigned int aRadius,
-        unsigned int aSegments,
+        int aErrorMax,
         int aIndex )
 {
-    return chamferFilletPolygon( CORNER_MODE::FILLETED, aRadius, aIndex, aSegments );
+    return chamferFilletPolygon( CORNER_MODE::FILLETED, aRadius, aIndex, aErrorMax );
 }
 
 
@@ -1703,12 +1704,12 @@ SHAPE_POLY_SET SHAPE_POLY_SET::Chamfer( int aDistance )
 }
 
 
-SHAPE_POLY_SET SHAPE_POLY_SET::Fillet( int aRadius, int aSegments )
+SHAPE_POLY_SET SHAPE_POLY_SET::Fillet( int aRadius, int aErrorMax )
 {
     SHAPE_POLY_SET filleted;
 
     for( size_t polygonIdx = 0; polygonIdx < m_polys.size(); polygonIdx++ )
-        filleted.m_polys.push_back( FilletPolygon( aRadius, aSegments, polygonIdx ) );
+        filleted.m_polys.push_back( FilletPolygon( aRadius, aErrorMax, polygonIdx ) );
 
     return filleted;
 }
@@ -1717,7 +1718,7 @@ SHAPE_POLY_SET SHAPE_POLY_SET::Fillet( int aRadius, int aSegments )
 SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::chamferFilletPolygon( CORNER_MODE aMode,
         unsigned int aDistance,
         int aIndex,
-        int aSegments )
+        int aErrorMax )
 {
     // Null segments create serious issues in calculations. Remove them:
     RemoveNullSegments();
@@ -1833,9 +1834,8 @@ SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::chamferFilletPolygon( CORNER_MODE aMode,
                     argument = 1;
 
                 double arcAngle = acos( argument );
-
-                // Calculate the number of segments
-                unsigned int segments = ceil( (double) aSegments * ( arcAngle / ( 2 * M_PI ) ) );
+                double arcAngleDegrees = arcAngle * 180.0 / M_PI;
+                int    segments = GetArcToSegmentCount( radius, aErrorMax, arcAngleDegrees );
 
                 double  deltaAngle  = arcAngle / segments;
                 double  startAngle  = atan2( -ys, xs );
diff --git a/include/convert_to_biu.h b/include/convert_to_biu.h
index 795f79d48a..b86f22ebd6 100644
--- a/include/convert_to_biu.h
+++ b/include/convert_to_biu.h
@@ -98,6 +98,15 @@ constexpr inline double Iu2Mils( int iu )
 {
     return iu / IU_PER_MILS;
 }
+
+// The max error is the distance between the middle of a segment, and the circle
+// for circle/arc to segment approximation.
+// Warning: too small values can create very long calculation time in zone filling
+// 0.05 to 0.005 mm are reasonable values
+
+constexpr int ARC_LOW_DEF  = Millimeter2iu( 0.02 );
+constexpr int ARC_HIGH_DEF = Millimeter2iu( 0.005 );
+
 #endif
 
 #endif  // CONVERT_TO_BIU_H_
diff --git a/include/geometry/geometry_utils.h b/include/geometry/geometry_utils.h
index 0c5cc3915b..a2d4f1d3c4 100644
--- a/include/geometry/geometry_utils.h
+++ b/include/geometry/geometry_utils.h
@@ -41,7 +41,7 @@
 int GetArcToSegmentCount( int aRadius, int aErrorMax, double aArcAngleDegree );
 
 /**
- * @return the correction factor to approximate a circle by seg�ments
+ * @return the correction factor to approximate a circle by segments
  * @param aSegCountforCircle is the number of segments to approximate the circle
  *
  * When creating a polygon from a circle, the polygon is inside the circle.
diff --git a/include/geometry/shape_poly_set.h b/include/geometry/shape_poly_set.h
index cec5870228..4d0c4ed6c8 100644
--- a/include/geometry/shape_poly_set.h
+++ b/include/geometry/shape_poly_set.h
@@ -1002,11 +1002,11 @@ class SHAPE_POLY_SET : public SHAPE
          * Function Fillet
          * returns a filleted version of the aIndex-th polygon.
          * @param aRadius is the fillet radius.
-         * @param aSegments is the number of segments / fillet.
+         * @param aErrorMax is the maximum allowable deviation of the polygon from the circle
          * @param aIndex is the index of the polygon to be filleted
          * @return POLYGON - A polygon containing the filleted version of the aIndex-th polygon.
          */
-        POLYGON FilletPolygon( unsigned int aRadius, unsigned int aSegments, int aIndex = 0 );
+        POLYGON FilletPolygon( unsigned int aRadius, int aErrorMax, int aIndex = 0 );
 
         /**
          * Function Chamfer
@@ -1020,10 +1020,10 @@ class SHAPE_POLY_SET : public SHAPE
          * Function Fillet
          * returns a filleted version of the polygon set.
          * @param aRadius is the fillet radius.
-         * @param aSegments is the number of segments / fillet.
+         * @param aErrorMax is the maximum allowable deviation of the polygon from the circle
          * @return SHAPE_POLY_SET - A set containing the filleted version of this set.
          */
-        SHAPE_POLY_SET Fillet(  int aRadius, int aSegments );
+        SHAPE_POLY_SET Fillet(  int aRadius, int aErrorMax );
 
         /**
          * Function DistanceToPolygon
@@ -1147,12 +1147,12 @@ class SHAPE_POLY_SET : public SHAPE
          * @param  aDistance is the chamfering distance if aMode = CHAMFERED; if aMode = FILLETED,
          *                   is the filleting radius.
          * @param  aIndex    is the index of the polygon that will be chamfered/filleted.
-         * @param  aSegments is the number of filleting segments if aMode = FILLETED. If aMode =
-         *                   CHAMFERED, it is unused.
+         * @param  aErrorMax is the maximum allowable deviation of the polygon from the circle
+         *                   if aMode = FILLETED. If aMode = CHAMFERED, it is unused.
          * @return POLYGON - the chamfered/filleted version of the polygon.
          */
         POLYGON chamferFilletPolygon( CORNER_MODE aMode, unsigned int aDistance,
-                                      int aIndex, int aSegments = -1 );
+                                      int aIndex, int aErrorMax = -1 );
 
         ///> Returns true if the polygon set has any holes that touch share a vertex.
         bool hasTouchingHoles( const POLYGON& aPoly ) const;
diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp
index 694ede18f6..1fd86ce2dd 100644
--- a/pcbnew/class_zone.cpp
+++ b/pcbnew/class_zone.cpp
@@ -1328,7 +1328,12 @@ bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly ) const
         break;
 
     case ZONE_SETTINGS::SMOOTHING_FILLET:
-        aSmoothedPoly = m_Poly->Fillet( m_cornerRadius, m_ArcToSegmentsCount );
+        // Note: we're now using m_ArcToSegmentsCount only as a hint to determine accuracy
+        // vs. speed.
+        if( m_ArcToSegmentsCount > SEGMENT_COUNT_CROSSOVER )
+            aSmoothedPoly = m_Poly->Fillet( m_cornerRadius, ARC_HIGH_DEF );
+        else
+            aSmoothedPoly = m_Poly->Fillet( m_cornerRadius, ARC_LOW_DEF );
         break;
 
     default:
diff --git a/pcbnew/convert_drawsegment_list_to_polygon.cpp b/pcbnew/convert_drawsegment_list_to_polygon.cpp
index 32e8bb9e08..ba82de7cf9 100644
--- a/pcbnew/convert_drawsegment_list_to_polygon.cpp
+++ b/pcbnew/convert_drawsegment_list_to_polygon.cpp
@@ -38,12 +38,6 @@
 #include <geometry/geometry_utils.h>
 
 
-// Error max when converting a circle or arc to segments.
-// Avoid too small values that create a very long calculation time
-// in zone fillings
-#define ARC_ACCURACY ( 0.05 * IU_PER_MM )
-
-
 /**
  * Function close_ness
  * is a non-exact distance (also called Manhattan distance) used to approximate
@@ -234,8 +228,8 @@ bool ConvertOutlineToPolygon( std::vector< DRAWSEGMENT* >& aSegList,
                 wxPoint  pstart = graphic->GetArcStart();
                 wxPoint  center = graphic->GetCenter();
                 double   angle  = -graphic->GetAngle();
-                int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_ACCURACY,
-                                                  (double)angle / 10.0 );
+                double   radius = graphic->GetRadius();
+                int      steps  = GetArcToSegmentCount( radius, ARC_LOW_DEF, angle / 10.0 );
                 wxPoint  pt;
 
                 for( int step = 1; step<=steps; ++step )
@@ -293,7 +287,7 @@ bool ConvertOutlineToPolygon( std::vector< DRAWSEGMENT* >& aSegList,
     // Output the Edge.Cuts perimeter as circle or polygon.
     if( graphic->GetShape() == S_CIRCLE )
     {
-        int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_ACCURACY, 360.0 );
+        int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_LOW_DEF, 360.0 );
         TransformCircleToPolygon( aPolygons, graphic->GetCenter(), graphic->GetRadius(), steps );
     }
     else
@@ -340,8 +334,8 @@ bool ConvertOutlineToPolygon( std::vector< DRAWSEGMENT* >& aSegList,
                     wxPoint pend    = graphic->GetArcEnd();
                     wxPoint pcenter = graphic->GetCenter();
                     double  angle   = -graphic->GetAngle();
-                    int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_ACCURACY,
-                                                      (double)angle / 10.0 );
+                    double  radius  = graphic->GetRadius();
+                    int     steps   = GetArcToSegmentCount( radius, ARC_LOW_DEF, angle / 10.0 );
 
                     if( !close_enough( prevPt, pstart, prox ) )
                     {
@@ -431,9 +425,9 @@ bool ConvertOutlineToPolygon( std::vector< DRAWSEGMENT* >& aSegList,
             // make a circle by segments;
             wxPoint  center  = graphic->GetCenter();
             double   angle   = 3600.0;
-            wxPoint  start = center;
+            wxPoint  start   = center;
             int      radius  = graphic->GetRadius();
-            int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_ACCURACY, 360.0 );
+            int      steps   = GetArcToSegmentCount( radius, ARC_LOW_DEF, 360.0 );
             wxPoint  nextPt;
 
             start.x += radius;
@@ -491,8 +485,8 @@ bool ConvertOutlineToPolygon( std::vector< DRAWSEGMENT* >& aSegList,
                         wxPoint pend    = graphic->GetArcEnd();
                         wxPoint pcenter = graphic->GetCenter();
                         double  angle   = -graphic->GetAngle();
-                        int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_ACCURACY,
-                                                         (double)angle / 10.0 );
+                        int     radius  = graphic->GetRadius();
+                        int     steps = GetArcToSegmentCount( radius, ARC_LOW_DEF, angle / 10.0 );
 
                         if( !close_enough( prevPt, pstart, prox ) )
                         {
diff --git a/pcbnew/pcbnew.h b/pcbnew/pcbnew.h
index 228a5fab03..8209549772 100644
--- a/pcbnew/pcbnew.h
+++ b/pcbnew/pcbnew.h
@@ -41,6 +41,10 @@
 #define ARC_APPROX_SEGMENTS_COUNT_LOW_DEF 16
 #define ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF 32
 
+// The new absolute-error-based algorithm uses the stored segment count as a hint on whether
+// to use ARC_HIGH_DEF or ARC_LOW_DEF.  This defines the crossover point.
+#define SEGMENT_COUNT_CROSSOVER 24
+
 /* Flag used in locate functions. The locate ref point is the on grid cursor or the off
  * grid mouse cursor. */
 #define CURSEUR_ON_GRILLE  (0 << 0)
diff --git a/pcbnew/specctra_import_export/specctra_export.cpp b/pcbnew/specctra_import_export/specctra_export.cpp
index dccf7f07f1..4e98f9c9c8 100644
--- a/pcbnew/specctra_import_export/specctra_export.cpp
+++ b/pcbnew/specctra_import_export/specctra_export.cpp
@@ -767,8 +767,7 @@ IMAGE* SPECCTRA_DB::makeIMAGE( BOARD* aBoard, MODULE* aModule )
                 double radius = GetLineLength( graphic->GetStart(), graphic->GetEnd() );
 
                 // seg count to approximate circle by line segments
-                int err_max = Millimeter2iu( 0.05 );
-                int seg_per_circle = GetArcToSegmentCount( radius, err_max, 360.0 );
+                int seg_per_circle = GetArcToSegmentCount( radius, ARC_LOW_DEF, 360.0 );
 
                 for( int ii = 0; ii < seg_per_circle; ++ii )
                 {
diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp
index 32a98fdb5f..87bcb9bc91 100644
--- a/pcbnew/zone_filler.cpp
+++ b/pcbnew/zone_filler.cpp
@@ -293,7 +293,7 @@ void ZONE_FILLER::buildZoneFeatureHoleList( const ZONE_CONTAINER* aZone,
     double correctionFactor;
 
     // Set the number of segments in arc approximations
-    if( aZone->GetArcSegmentCount() == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF  )
+    if( aZone->GetArcSegmentCount() > SEGMENT_COUNT_CROSSOVER  )
         segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF;
     else
         segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF;