<style> .reveal-up, .reveal-left, .reveal-right, .reveal-stagger > * { opacity: 1 !important; transform: none !important; } </style>
  1. Learn
  2. BIM Math
  3. Foundations
All lessons in this course

Lesson 3 of 44 15 minBeginner

Units, precision and tolerance

Internal units vs display units, why 0.1 + 0.2 is not 0.3, why float is dangerous far from the origin, and how to compare coordinates with a tolerance.

Every number in a model has a unit, and every number in a computer has limited precision. Both cause real bugs: a wall that is 304.8 times too long, two points that “should” be equal but are not, a model that jitters in a viewer. This lesson covers the three habits that prevent them.

Internal units and display units

BIM software stores lengths in one internal unit and converts only when it shows or reads a value. Revit, for example, stores every length in decimal feet, even in a metric project; the millimetres you type are converted on the way in. IFC files declare their units in the file, and a reader must apply them.

ConversionExact factor
1 inch25.4 mm (exact by definition)
1 foot304.8 mm (12 × 25.4)
1 metre1000 mm ≈ 3.28084 ft
1 square foot0.09290304 m²
Units.cs
const double MmPerFoot = 304.8; static double MmToFeet(double mm) => mm / MmPerFoot;static double FeetToMm(double ft) => ft * MmPerFoot; double wallFt = MmToFeet(3000);Console.WriteLine(wallFt);Console.WriteLine(FeetToMm(wallFt));
Output
9.84251968503937
3000

Floating point is not exact

A double stores about 15–17 significant digits in binary. Many simple decimals, such as 0.1, have no exact binary form, just as 1/3 has no exact decimal form. Each operation rounds to the nearest representable value, and those tiny errors add up.

Drift.cs
double sum = 0;for (int i = 0; i < 10; i++) sum += 0.1; Console.WriteLine(sum);Console.WriteLine(sum == 1.0);Console.WriteLine(0.1 + 0.2);
Output
0.9999999999999999
False
0.30000000000000004

Geometry hits this constantly. Rotate a point a quarter turn and the X that should be zero is not quite zero:

AlmostZero.cs
double angle = Math.PI / 2;                     // 90 degreesdouble x = 10 * Math.Cos(angle), y = 10 * Math.Sin(angle); Console.WriteLine($"({x}, {y})");Console.WriteLine(x == 0);
Output
(6.123233995736766E-16, 10)
False

Compare with a tolerance

The fix is to never test computed coordinates with ==. Ask instead whether they are close enough: within a tolerance chosen for the job.

Tolerance.cs
using P3 = (double X, double Y, double Z); const double Tol = 1e-9; static bool Near(double a, double b) => Math.Abs(a - b) <= Tol;static bool SamePoint(P3 p, P3 q) => Near(p.X, q.X) && Near(p.Y, q.Y) && Near(p.Z, q.Z); double x = 10 * Math.Cos(Math.PI / 2);P3 wallEnd = (x, 10, 0);P3 nextWallStart = (0, 10, 0); Console.WriteLine(wallEnd == nextWallStart);Console.WriteLine(SamePoint(wallEnd, nextWallStart));
Output
False
True

Picking the tolerance is a modelling decision, not a maths one:

  • Too small (say 1e-15) and normal rounding errors make equal points look different, so walls fail to join.
  • Too large (say 1 mm in a model measured in mm) and genuinely different points merge, so small details vanish.
  • A good absolute tolerance sits far above rounding noise and far below the smallest real detail. For building-scale models in metres, 1e-9 to 1e-6 is typical; BIM kernels use a fixed tolerance tied to their internal unit.
  • For values that span huge ranges (areas of a bolt and of a site), a relative tolerance, Math.Abs(a - b) <= tol * Math.Max(Math.Abs(a), Math.Abs(b)), works better.

float vs double: precision depends on distance from the origin

Floating-point numbers are dense near zero and sparse far away. The gap between neighbouring representable values grows with the size of the number. A float has only about 7 significant digits, so 10 km from the origin, measured in millimetres, it cannot even represent a whole-millimetre step reliably.

FarFromOrigin.cs
float farF = 10_000_000f;          // 10 km in millimetres, as floatdouble farD = 10_000_000.0;        // the same, as double Console.WriteLine(farF + 0.3f == farF);Console.WriteLine(farD + 0.3 == farD);
Output
True
False

Adding 0.3 mm to the float changes nothing at all: the detail is lost. Try it below: move the slider from the origin out to site scale and watch the gap between representable values.

Interactive

Precision and tolerance

A coordinate stored in millimetres. How far apart are the neighbouring values a float and a double can hold at this distance from the origin?

One millimetre at this distance, with every value a float can store marked
xx + 1 mm
TypeGap between values0.1 mm detail?
float1 mmno
double0.00186 nmyes
At 10 km a float can only step in 1 mm, so x + 0.3 mm == x: the detail vanishes. A double still steps in 0.00186 nm. This is why geometry should use double and stay near the origin.

Round for display, not for storage

Show 6.12E-16 as 0.00 with :F2, but keep the stored value as it is. Rounding stored coordinates every time adds a new error on each edit and can push points across a tolerance boundary.

Quick check

Why does sum == 1.0 print False after adding 0.1 ten times?

Quick check

A model in millimetres uses a tolerance of 5 mm to decide whether two points are the same. What can go wrong?

Quick check

Why do models placed at real-world survey coordinates often jitter in viewers?

Practice

Write ToFeetInches(double mm) that formats a length in feet and whole inches, rounded to the nearest inch, like 9' 10". Test it with 3000 mm and 2743 mm. Watch out for a value that rounds up to 12 inches.

Show a hint

Convert to total inches first (mm / 25.4), round that, then split into feet (/ 12) and inches (% 12). Rounding the total first means you can never get 12 inches.

Show the solution
Solution.cs
static string ToFeetInches(double mm){    int totalInches = (int)Math.Round(mm / 25.4);    int feet = totalInches / 12;    int inches = totalInches % 12;    return $"{feet}' {inches}\"";} Console.WriteLine(ToFeetInches(3000));Console.WriteLine(ToFeetInches(2743));
Output
9' 10"
9' 0"

Remember

  • Store lengths in one internal unit and convert only at the edges; Revit uses decimal feet internally.
  • Doubles round: 0.1 + 0.2 is 0.30000000000000004, and computed coordinates are rarely exactly equal.
  • Never compare computed coordinates with ==; use a tolerance far above rounding noise and far below real details.
  • Precision falls with distance from the origin; float loses millimetres at site scale, so use double and keep geometry near the origin.
  • Round when you display, not when you store.
0 of 44 lessons completed