Early preview: syntax, standard library APIs, and tooling may change.

Core DataFixed Point

Fixed Point

Fixed is Blorp's fixed-point decimal type for values that need an explicit decimal scale.

Creating Fixed Values

Import fixed as F, then create decimal values with a scale.

import:
	fixed as F


func main(args: List[String]) -> Void:
	price: Fixed = F.fixed(19.99, 2)

	tax_rate: Fixed = F.fixed(0.0825, 4)

	whole_dollars: Fixed = F.from_int(42, 2)
-- F.to_string(price) == "19.99"
-- F.to_string(tax_rate) == "0.0825"
-- F.to_string(whole_dollars) == "42.00"

Scale and Precision

scale is the number of decimal places. fixed uses default precision; with_precision sets it explicitly.

import:
	fixed as F


func main(args: List[String]) -> Void:
	price: Fixed = F.fixed(19.99, 2)

	custom: Fixed = F.with_precision(1.2345, 4, 6)

	price_scale: Int = F.get_scale(price)

	custom_precision: Int = F.get_precision(custom)
-- price_scale == 2
-- custom_precision == 6

Arithmetic

+ and - normalize scale, * adds scales, and / uses extra precision. Round when the domain expects a specific scale.

import:
	fixed as F


func main(args: List[String]) -> Void:
	price: Fixed = F.fixed(19.99, 2)

	quantity: Fixed = F.from_int(3, 0)

	subtotal: Fixed = price * quantity

	tax: Fixed = F.round_to(subtotal * F.fixed(0.0825, 4), 2)

	total: Fixed = subtotal + tax
-- F.to_string(subtotal) == "59.97"
-- F.to_string(tax) == "4.95"
-- F.to_string(total) == "64.92"

Rounding

round_to returns a new Fixed at the requested scale. to_int truncates toward zero.

import:
	fixed as F


func main(args: List[String]) -> Void:
	raw: Fixed = F.fixed(19.999, 3)

	rounded: Fixed = F.round_to(raw, 2)

	whole: Int = F.to_int(rounded)
-- F.to_string(rounded) == "20.00"
-- whole == 20

Comparisons

Comparisons normalize scale, so the same decimal value compares equal even when stored with different scales.

import:
	fixed as F


func main(args: List[String]) -> Void:
	one_decimal: Fixed = F.fixed(10.0, 1)

	two_decimals: Fixed = F.fixed(10.0, 2)

	smaller: Fixed = F.fixed(9.99, 2)

	same_value: Bool = one_decimal == two_decimals

	ordered: Bool = smaller < one_decimal
-- same_value == True
-- ordered == True

FixedPoint Bounds

Fixed is the concrete type. Use T: FixedPoint only when a helper should work over any fixed-point type in the family.

import:
	fixed as F: round_to


pure func cents[T:FixedPoint](value: T) -> T:
	round_to(value, 2)


func main(args: List[String]) -> Void:
	rounded: Fixed = cents(F.fixed(19.999, 3))
-- F.to_string(rounded) == "20.00"

Conversions

Use to_string for decimal text. Convert to Float only when handing a value to APIs that require floats.

import:
	fixed as F


func main(args: List[String]) -> Void:
	value: Fixed = F.fixed(12.25, 2)

	as_text: String = F.to_string(value)

	as_int: Int = F.to_int(value)

	as_float: Float = F.to_float(value)
-- as_text == "12.25"
-- as_int == 12
-- as_float == 12.25

Example

fixed.brp
import:
	fixed as F


pure func money(value: Float) -> Fixed:
	F.fixed(value, 2)


pure func rate(value: Float) -> Fixed:
	F.fixed(value, 4)


pure func line_total(unit_price: Fixed, quantity: Int) -> Fixed:
	unit_price * F.from_int(quantity, 0)


pure func tax_for(subtotal: Fixed, tax_rate: Fixed) -> Fixed:
	F.round_to(subtotal * tax_rate, 2)


func main(args: List[String]) -> Void:
	price: Fixed = money(19.99)
	quantity: Int = 3
	subtotal: Fixed = line_total(price, quantity)
	tax: Fixed = tax_for(subtotal, rate(0.0825))
	total: Fixed = subtotal + tax
	scale: Int = F.get_scale(total)

	print("subtotal: ${subtotal}") -- prints: subtotal: 59.97
	print("tax: ${tax}") -- prints: tax: 4.95
	print("total: ${total}") -- prints: total: 64.92
	print("scale: ${scale}") -- prints: scale: 2

Try it

terminal
blorp run examples/docs/core-data/fixed/fixed.brp