Numbers
10th January 2026
This article describes the implementation of numbers in the 32-bit versions of uLisp.
Integers
Integers can be between 2147483647 and -2147483648.
Floating point
uLisp uses the standard 32-bit single-precision floating-point format which uses 1 bit for the sign, 8 bits for the exponent which can be from -126 to +127, and 23 bits for the mantissa which can be from 0 to 8388607. The largest number you can represent is about 2127 ≈ 1.7014e38. If you create a result greater than this uLisp will display Inf for infinity:
> 1e39 Inf
The smallest number you can represent is about 2-126 ≈ 1.1755e-38. Numbers smaller than this will be treated as zero.
Precision
The precision of a floating-point number is the difference between that number and the next number you can represent. Floating point format is peculiar in that the precision is very high near zero, and it decreases in steps as you move towards the most positive (or most negative) number it can represent, because there are exactly 8388608 possible floating-point values between each pair of powers of two.
For example, what is the precision of 7.5 in floating-point format? This lies between 4 and 8, and there are 8388608 possible floating-point values between 4 and 8, so the precision is:
Between 8388608 and 1677216 the precision is 1, so you can only represent integers exactly.
Precision pitfalls
Above 1677216 the precision is worse than 1, which gives rise to some strange results; for example:
> (+ 2147483640 10 -2147483640 10) 10.0
We would expect the answer 20 so what's going on here?
The explanation is that the sum is evaluated left to right; 2147483640+10 is larger than the largest integer, so it's converted to floating point:
> (+ 2147483640 10) 2.14748e9
The result lies between 2147483648 and 4294967296 where, using the above calculation, the precision is 256, so adding 10 to 2147483640 didn't change it, and adding -2147483640 gave 0.0. Finally, adding 10 gave 10.0.
Doing the sum in a different order can give the correct result:
> (+ 2147483640 -2147483640 10 10) 20
Take care when doing floating-point calculations with numbers of dramatically different magnitudes.
For an excellent explanation of floating point see Fabian Sanglard's Floating Point Visually Explained.
Conversion between integer and floating point
uLisp aims to keep calculations in integer format wherever possible. When a result can't be represented as a 32-bit integer it is automatically converted to floating point.
uLisp never converts a floating-point result back to integer format.
Some special cases
Minus and abs
Some calculations will give a floating-point result where you might not expect it. For example, applying '-' or abs to an integer can give a floating point result:
> (- -2147483648) -2.14748e9
because 2147483648 cannot be represented as an integer.
Raise to power
If the arguments to the expt (raise to power) function are integers it gives an integer result where possible:
> (expt 3 19) 1162261467
> (expt -1 100)
1
but a floating-point result otherwise:
> (expt 2 31)
2.14748e9
Truncate, floor, and ceiling
The function truncate:
(truncate n m)
is equivalent to:
(truncate (/ n m))
However, giving the divisor as a second parameter allows uLisp to make truncate provide a more accurate answer. For example:
(/ 123456789 10)
when expressed as a 32-bit floating-point number doesn't have the 8 digits of precision to give the correct answer of 12345678:
> (truncate 12345678.9) 12345679
However, the call:
(truncate 123456789 10)
where the parameters are 32-bit integers, returns a precise result [1]:
> (truncate 123456789 10) 12345678
The same is true of floor and ceiling.
- ^ As of ARM 4.8e and ESP 4.8f.
