New to Attio?Get 10% off when you sign up through Craftt.Try Attio free →
All articles

The round(), ceil(), and floor() functions in Attio: board-deck numbers, seat bundles, and honest tenure

Written by

Published 7 min readTested in live Attio workspaces
Contents10 sections
  1. What round(), ceil(), and floor() do
  2. round(): numbers for humans
  3. ceil(): numbers for billing
  4. floor(): numbers for progress
  5. Picking the direction
  6. Composing with the rest of the library
  7. CRM use cases that earn their keep
  8. Copy-paste formulas
  9. Gotchas
  10. Final thoughts

Formulas produce exact numbers, and exact numbers are usually the wrong deliverable. A weighted pipeline of $19,847.3333 can't go in a board deck; 4.2 seat bundles can't go on an invoice; 5.8 months as a customer is not six months, whatever rounding says. round(), ceil(), and floor() are the three ways to trim a number — and choosing between them is really choosing *which direction it's safe to be wrong in*.

This is part of our function-by-function series on Attio formula attributes — previously: if(), timeSpentIn(), dateDiff(), the ?? operator, count(), hasBeenIn(), valueSetAt(), contains(), sum(), previousValue(), dateAdd(), formatDate(), min()/max(), valueAt(), replace()/replaceAll(), and avg()/median(). This one covers the three rounding functions together, because the interesting part is the choice between them.

color:var(--color-text-heading)]">Prefer to watch? There are short videos for these: [Attio ceil(): when rounding down loses you money and Attio floor(): tenure in whole months, not 11.7. The whole library is also being built end to end in our first live session — Attio Formulas: Every Function, Built Live, free, no registration, Thursday 8 October 2026.

What round(), ceil(), and floor() do

One rounds to the nearest, one always up, one always down:

round(value, num_digits)
ceil(value)
floor(value)

round(1234.5678, 2) returns 1234.57. ceil(7.2) returns 8. floor(9.8) returns 9. Only round() takes a digits argument — ceil() and floor() always land on a whole number, which fits their jobs: things you count in units.

Force the output type to Number (or Currency for money) rather than leaving it on Auto.

round(): numbers for humans

round() is presentation. The math behind a weighted pipeline is exact and should stay exact — but the number a human reads shouldn't be:

round({Deal value} * 0.4, 0)

$19,847.3333 becomes $19,847, and the board deck stops looking like a debugger. Two digits for currency that keeps cents, zero for anything a person scans in a view. The rule: round() never goes into further math if you can help it — it's the last step before eyes, not an input to the next formula. Round once, at the end.

ceil(): numbers for billing

ceil() is the commercial one. Anything sold in units — seats in packs, licenses, storage blocks, support hours — has the same shape: the customer's need is fractional, the thing you sell isn't, and the fraction always costs a whole unit.

ceil({Users} / 10)

42 users, packs of 10: ceil(4.2) = 5 bundles. round() here would say 4 — and undercharge every customer whose need lands in the bottom half of a pack. That's the reason ceil() exists as a separate function: when the direction of the error has a price, "nearest" is not neutral.

floor(): numbers for progress

floor() is the honest one. Elapsed time, completed units, anything earned — the partial one doesn't count until it's finished:

floor(dateDiff({Signup date}, today(), "months"))

A customer 5.8 months in has completed 5 months, not 6. Tenure segments, anniversary triggers, "eligible for renewal pricing after 12 full months" — all of these break subtly with round(), because for half of each period it grants the milestone early. floor() is also the "full quarters as a customer" and "complete years since founding" function, for the same reason.

Picking the direction

The three functions are one decision table:

The number is for…Wrong-direction costUse
Reading — decks, views, reportsNone; it's displayround(x, 0)
Charging — seats, packs, blocksRounding down underchargesceil(x)
Progress — tenure, completed unitsRounding up overstatesfloor(x)

If trimming the number changes what someone pays or qualifies for, it's not formatting — pick the direction deliberately. If it doesn't, it's round().

Composing with the rest of the library

Rounding is a finishing move, so it wraps the others. Clean up an avg() rollup for display:

round(avg({Deal value}) ?? 0, 0)

Floor a dateDiff() for tenure, then route on it with if():

if(floor(dateDiff({Signup date}, today(), "months")) >= 12, "Anniversary pricing", "Standard")

The ?? guard goes *inside* the rounding — blank-proof the input, then trim the result. And the pillar's random() sampler is the same pattern: round(random() * 100, 0) turns a raw random into a clean 0–100 score for picking audit samples.

CRM use cases that earn their keep

  • Board-deck weighted pipeline — round({Deal value} * 0.4, 0) — important because stakeholders read the number, and $19,847.3333 reads as noise, not precision.
  • Seat bundles to quote — ceil({Users} / 10) — important because you never sell 4.2 bundles, and rounding down is a discount nobody approved.
  • Full months as a customer — floor(dateDiff({Signup date}, today(), "months")) — important because tenure milestones granted early — even by half a month — compound across every account.
  • Anniversary-pricing flag — if(floor(dateDiff({Signup date}, today(), "months")) >= 12, "Anniversary pricing", "Standard") — important because eligibility rules need completed months, and this one can't fire early.
  • Clean average deal size — round(avg({Deal value}) ?? 0, 0) — important because the rollup's long tail of decimals hides the actual magnitude at a glance.
  • Support blocks consumed — ceil({Hours used} / 5) — important because a 5-hour block that's 20 minutes used is a consumed block, and billing math has to agree with the contract.

Copy-paste formulas

Swap in your attribute names and these work as-is:

Weighted pipeline, deck-ready (Currency output):

round({Deal value} * 0.4, 0)

Seat bundles needed, packs of 10 (Number output):

ceil({Users} / 10)

Full months as a customer (Number output):

floor(dateDiff({Signup date}, today(), "months"))

Anniversary-pricing flag (Text output):

if(floor(dateDiff({Signup date}, today(), "months")) >= 12, "Anniversary pricing", "Standard")

Average deal size, clean and blank-safe (Currency output):

round(avg({Deal value}) ?? 0, 0)

Gotchas

  • Only round() takes digits. ceil() and floor() always go to the nearest integer. To ceil to one decimal, scale: ceil({x} * 10) / 10.
  • Round last. Rounding an input and then multiplying compounds the error. Keep intermediate formulas exact; trim in the final, human-facing attribute.
  • round() at the midpoint. Halfway values have to break one way; if a threshold sits exactly on a .5 boundary, test a record on it rather than assuming.
  • color:var(--color-text-heading)]">Blank in, blank out. All three pass null through. Guard the input with [?? before rounding, not after.
  • Daily refresh for date math. Anything built on today() recalculates around midnight UTC, so a tenure floor flips the day the month completes — fine for milestones, not for minute-level countdowns.
  • Nesting limit. Three formula attributes deep. Wrap the rounding in the same formula as the math rather than adding a rounding-only attribute on top of a stack.

Final thoughts

round(), ceil(), and floor() look like formatting; two of them are policy. Trim for the reader with round(), charge in whole units with ceil(), grant milestones on completed periods with floor() — and any time the direction of the error has a cost, make sure the function you picked errs the way the business does.

For the rest of the library — every history, logic, math, date, and text function with CRM use cases — see the complete guide to Attio formula attributes.

And if you'd rather have your billing math, tenure segments, and deck-ready rollups designed and shipped for you, that's literally what we do. Get a free workspace audit or see the AI-native Attio sprint.

Official sources

Attio documentation used to verify this guide:

Need help with your Attio setup?

We migrate teams, build data models, wire automations, and train Claude agents inside your workspace. Discovery call is free.

Ready when you are.

Book a call and we will tell you honestly whether this is worth doing, or start with the free 48-hour audit and decide afterwards.