Float

A 'float' stores a number with a decimal point, like 3.14 or -0.001. It uses 64-bit precision (the same as f64 in Rust), which gives you about 15 significant digits — enough for scientific work, games, and most real-world maths. When you write a number with a decimal point in Loft, it is automatically a float.

fn main() {

Write a decimal point in a literal and Loft treats the whole expression as a float. The usual arithmetic operators (+, -, *, /) all work the way you would expect.

  assert(1.5 + 0.5 == 2.0, "Float addition");
  assert(3.0 / 2.0 == 1.5, "Float division");
  assert(2.0 * 1.5 == 3.0, "Float multiplication");

The 'f' suffix selects single-precision (32-bit) floats. Single-precision takes half the memory of a regular float and is common in graphics and audio code where exact decimal values matter less than speed or size.

  x = 0.1f + 2 * 1.0f;
  assert(x == 2.1f, "Single precision float");

'as float' converts an integer to a float so you can mix them in calculations. Putting a float inside '{...}' converts it to text for display. You can also go the other way: '"1.5" as float' parses the text back to a number.

  assert(3 as float == 3.0, "Integer to float");
  assert("1.5" as float == 1.5, "Text to float");
  assert("{1.5}" == "1.5", "Float formatted as text");

Formatting Floats

Put a colon after the value inside '{...}' to control how it looks. '{value:width.precision}' — 'width' is the minimum number of characters printed (padded with spaces on the left); 'precision' fixes the number of decimal places. You can use either part on its own: '{value:.2}' just fixes decimal places, '{value:5}' just sets the minimum width.

  assert("{1.2:4.2}" == "1.20", "Float with width and precision");
  assert("{334.1:.2}" == "334.10", "Float with precision only");
  assert("{1.4:5}" == "  1.4", "Float with width only");

Math Functions

'PI' is a built-in constant (approximately 3.14159265358979). Multiplying it by 1000 and rounding gives 3142, which confirms the value is correct.

  assert(round(PI * 1000.0) == 3142.0, "PI constant");

'pow(base, exponent)' raises a number to a power — this is exponentiation. Note: '^' in Loft is bitwise XOR, NOT exponentiation. Always use pow() for powers. 'log(value, base)' is the inverse: log(x, b) answers "b to the what power equals x?" Here: 4^5 = 1024, and log(1024, 2) = 10 because 2^10 = 1024.

  assert(log(pow(4.0, 5), 2) == 10.0, "log base 2 of 4^5");

'sin' and 'cos' work in radians, not degrees. A full circle is 2*PI radians; a half circle (180 degrees) is PI radians. sin(PI) is theoretically zero but floating-point gives a tiny rounding error, so we use ceil() to snap it up. cos(PI) is exactly -1, so the whole expression ceil(~0 + -1 * 1000) lands at -1000.

  assert(ceil(sin(PI) + cos(PI) * 1000) == -1000.0, "sin and cos");

'abs()' returns the absolute value — the distance from zero, always non-negative. Useful any time you care about magnitude but not direction.

  assert(abs(-2.5) == 2.5, "Absolute value of float");

'round()' picks the nearest whole number (0.5 rounds up). 'ceil()' always rounds up to the next whole number, even for 2.01. 'floor()' always rounds down to the previous whole number, even for 2.99. All three give back a float, not an integer — so 3.0, not 3.

  assert(round(2.6) == 3.0, "round up");
  assert(round(2.4) == 2.0, "round down");
  assert(ceil(2.1) == 3.0, "ceil");
  assert(floor(2.9) == 2.0, "floor");

Single-precision floats work inside format strings just like regular floats.

  assert("a{0.1f + 2 * 1.0f}b" == "a2.1b", "Single-precision format");
}