Numbers: integer division, rounding and decimal precision
Arithmetic on whole numbers and decimals: why 17 / 5 is 3, how ROUND, CEILING and FLOOR behave (also with negatives), what type a calculation returns, and how to avoid dividing by zero.
ints give an int; mix in a decimal and you get a decimal with a precision and scale worked out by rules. Most surprises in calculations come from those two facts.
Integer division and remainder
SELECT 1050 / 200 AS FullBoxes, 1050 % 200 AS LeftOver, 1050 / 200.0 AS ExactBoxes, CEILING(1050 / 200.0) AS BoxesToBuy;FullBoxes LeftOver ExactBoxes BoxesToBuy
--------- -------- ---------- ----------
5 50 5.250000 6ints, / gives the whole part and % the remainder. Writing 200.0 makes it a decimal, so the division keeps its fraction. CEILING then rounds up, because you cannot buy a quarter of a box.
Rounding
| Function | Does | 2.5 | -2.5 |
|---|---|---|---|
ROUND(x, 0) | |||
ROUND(x, 0, 1) | |||
CEILING(x) | |||
FLOOR(x) | |||
CAST(x AS int) |
ROUND keeps the original type, so its result still has the same number of decimal places, filled with zeros. To store or show a price with exactly two places, cast it:
SELECT Name, Price, Price * 1.2 AS RawIncVat, ROUND(Price * 1.2, 2) AS Rounded, CAST(Price * 1.2 AS decimal(10,2)) AS IncVatFROM dbo.ProductsWHERE CategoryId = 1;Name Price RawIncVat Rounded IncVat ---------------- ----- --------- ------- ------ Claw hammer 18.50 22.200 22.200 22.20 Cordless drill 89.00 106.800 106.800 106.80 Tape measure 8 m 9.75 11.700 11.700 11.70
What type does a calculation return?
SELECT SQL_VARIANT_PROPERTY(CAST(10 AS decimal(10,2)) * CAST(3 AS decimal(10,2)), 'Scale') AS MultiplyScale, SQL_VARIANT_PROPERTY(CAST(10 AS decimal(10,2)) / CAST(3 AS decimal(10,2)), 'Scale') AS DivideScale, CAST(10 AS decimal(10,2)) / CAST(3 AS decimal(10,2)) AS Quotient;MultiplyScale DivideScale Quotient ------------- ----------- --------------- 4 13 3.3333333333333
Line totals and percentages
SELECT OrderId, ProductId, Quantity, UnitPrice, Quantity * UnitPrice AS LineTotal, Quantity * 100 / 40 AS PctOf40Int, Quantity * 100.0 / 40 AS PctOf40FROM dbo.OrderItemsWHERE OrderId = 4;OrderId ProductId Quantity UnitPrice LineTotal PctOf40Int PctOf40
------- --------- -------- --------- --------- ---------- ----------
4 6 40 6.80 272.00 100 100.000000
4 7 6 42.00 252.00 15 15.000000Quantity * 100 / 40 is all int, so 6 × 100 / 40 = 15, not 15.0. Writing 100.0 makes the whole expression decimal. Where you put the .0 matters: Quantity * 100 / 40.0 would also work, but Quantity / 40 * 100.0 would already have lost the fraction in Quantity / 40.
Dividing by zero
NULLIF(x, 0) turns a zero divisor into NULL, and dividing by NULL simply gives NULL:
SELECT Name, Price, Stock, Price / NULLIF(Stock, 0) AS PricePerUnitInStockFROM dbo.ProductsWHERE ProductId IN (8, 9, 12);Name Price Stock PricePerUnitInStock ----------------- ----- ----- ------------------- Wood screws (200) 11.90 150 0.0793333333333 Wall plugs (100) 4.50 0 NULL Safety goggles 6.80 0 NULL
Other maths functions
SELECT ABS(-4.5) AS AbsValue, POWER(2, 10) AS TwoToTen, SQRT(3.0 * 3.0 + 4.0 * 4.0) AS Hypotenuse, ROUND(PI() * POWER(0.15, 2), 4) AS ColumnAreaM2, SIGN(-7) AS SignOfMinus7;AbsValue TwoToTen Hypotenuse ColumnAreaM2 SignOfMinus7
-------- -------- ---------- ------------ ------------
4.5 1024 5 0.0628 -1Boxes, the total cost as Cost, and how many plugs will be spare as Spare.
Show a hint
Show the solution
SELECT CEILING(2300 / 100.0) AS Boxes, CEILING(2300 / 100.0) * 4.50 AS Cost, CEILING(2300 / 100.0) * 100 - 2300 AS Spare;Boxes Cost Spare ----- ------ ----- 23 103.50 0
Remember
int / int is an int: 17 / 5 = 3, and % gives the remainder. Add .0to one side to keep fractions.ROUND rounds halves away from zero and keeps the scale; CEILING goes up, FLOOR goes down, CAST to int cuts off. Decimal results have their own precision and scale: multiplying adds scales, dividing gives at least 6 places. Cast to decimal(10,2)to store or show a value with exactly two places.x / NULLIF(y, 0)avoids a divide-by-zero error.