Log In  

Have you ever wanted to use numbers that were bigger or smaller than the range you're given in PICO-8? Perhaps you want some more digits of precision for the fractional part of your numbers. Or maybe you're interested in doing math in a severely inefficient way...

Introducing, the Decimal Floating Point library for PICO-8!

Cart #wisogipuji-1 | 2020-08-25 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
7

(This example cart calculates the value of pi in all three "datatypes", using Viète's formula.)

Wait, what? Doesn't Lua have floating point already?

Not PICO-8's Lua. It actually uses 16.16 fixed point numbers. Because of this lack of floating-point numbers on PICO-8, I decided to make this library to fill that void (not that anyone asked it to be filled, but still).

What are floating point numbers?

Without getting too much into the nitty-gritty, floating point numbers are numbers that are represented in the following form:

sign * coefficient * (base ^ exponent)

where sign is either 1 or -1, and (in the case of this library) base is 10. Floating point numbers are meant to give a trade-off between range and precision.

How do I start using this library?

In order to declare a new float, you can use any one of df_single(), df_double(), or df_quad(). "Single" is the lowest precision available, with 7 significant digits; "double" is the next-lowest precision, with 16 significant digits; and "quad" is the highest precision available, with 34 significant digits.

By default, the way to declare floats is by giving the sign, coefficient, and exponent to the constructors in a table. The sign can either be "+" or "-", the coefficient is given as a string of decimal digits, and the exponent is a number representing the power of 10 to multiply the coefficient by.

For example, to create a single-precision float representing the number 256 (or equivalently, +256 * 10^0):

df_single({"+","256",0})

To create a double-precision float representing the number -3.14159 (or equivalently, -314159 * 10^-5):

df_double({"-","314159",-5})

To create a quad-precision float representing the number 116.9 billion (or equivalently, 1169 * 10^8):

df_quad({"+","1169",8})

Note: this means that a number with trailing zeros can have multiple representations (e.g. {"+","1169",8}, {"+","11690",7}, or {"+","1169000",5}), depending on how many digits of accuracy are given.

If you add a dash to the --[[ comment in tab 2 to uncomment the tab, you can also supply strings (or anything that can be turned into a string with tostr()), like these:

df_single("12")
df_double("-76")
df_single("+3.14159")
df_quad("0.003")
df_single("4e9")
df_double("0.73e-7")

If you want to declare a value of Infinity, have the exponent be the string "inf" (the coefficient doesn't really matter). If string parsing is enabled, this can also be done by passing the single string "inf" or "infinity".
If you want to declare a value of NaN, have the exponent be the string "qnan" (if you provide a coefficient, it can be retrieved via the df_payload() function). If string parsing is enabled, this can also be done by passing the single string "nan", with optional "payload" digits at the end.
If you want to declare a value of sNaN ("signaling NaN", which will error out on most arithmetic operations), have the exponent be the string "snan" (if you provide a coefficient, it can be retrieved via the df_payload() function). If string parsing is enabled, this can also be done by passing the single string "snan", with optional "payload" digits at the end.

How do I display a float?

To convert a float to a string, use df_tostr(val), where val is your float.

How do I do mathematical operations to floats?

The following functions return a new float with the value of the result:

To add two floats, use df_add(val1, val2).
To subtract two floats, use df_subtract(val1, val2).
To multiply two floats, use df_multiply(val1, val2).
To divide two floats, use df_divide(val1, val2).
To integer-divide two floats, use df_div(val1, val2).
To get the remainder of division of two floats, use df_mod(val1, val2).
To get the integer-quotient and remainder simultaneously, use df_divmod(val1, val2). (Note: this returns two values - the integer-quotient, then the remainder.)
To get the integer part of a float, use df_ipart(val).
To get the fractional part of a float, use df_fpart(val).
To reverse a float's sign, use df_unm(val).

The following functions return a true or false value depending on the result of the comparison:

To check for equality between two floats, use df_eq(val1, val2).
To check if one float is less than another float, use df_lt(val1, val2).
To check if one float is less than or equal to another float, use df_le(val1, val2).

Note: floats are represented as tables; thus, in order to copy the value of a float into another variable, the table will need to be cloned by value. Luckily, you can use the included function clone(tbl), which returns a shallow copy of a table, to clone float values as well.

Is there anything else I can do with floats?

Yes, there is!

To get the sign of a float, use df_sign(val). (Note: to return 0 for a float representing 0, use df_sign(val, true) instead. And yes, this implies that a "negative zero" is a possible value.)
To check if a float is a finite number (i.e. not Infinity, NaN, or sNaN), use df_is_finite(val).
To check if a float is either NaN or sNaN, use df_is_nan(val).
To check if a float is sNaN, use df_is_snan(val).
To get the "payload" of a NaN or sNaN, use df_payload(val). (Note: returns nil if the number is not a NaN or sNaN; returns "0" if no payload has been provided.)
To round a float to p significant digits, use df_round(val, p). (Note: omit the p argument to round it to the given precision for the number.)
To convert a float to a string, use df_tostr(val).

If you add a dash to the --[[ comment in tab 1 to uncomment the tab, the following functions become available:

To convert a float to a table of byte values (4 for single-precision, 8 for double-precision, and 16 for quad-precision), use df_tobytes(val).
To convert a table of byte values to a float, use df_from(bytes). (Note: the table will be assumed to contain the proper amount of bytes for the desired float precision: 4 for single-precision, 8 for double-precision, and 16 for quad-precision.)

I have a question/comment that isn't addressed here!

Good! Reply to this post to tell me about it (or tell me in the Discord server, my tag is JWinslow23#6531).

How are you going to end this post?

I...um...
...shoot...I didn't think of that...I'm terrible at ending forum posts...

Version History

v1.0.1 (August 25, 2020)

  • Token optimizations, fixed bug when dividing by infinity

v1.0 (August 23, 2020)

  • Initial release
P#81072 2020-08-23 06:04 ( Edited 2020-10-27 04:49)

:: Trip

Hi

Thanks for this - I actually can use this! However, being new to Pico-8 and all, I am unsure how to install the library. I take it that it is not like Arduino?

I just need the decimal floating point-part, not the example with pi and such.

Thanks!

P#86544 2021-01-17 22:03

@Trip "Installing" is really just as simple as copy-pasting the code of this cart into a new cart, or using the #include command to include it's code in your project (check the readme for PICO-8 for details). It's not really a "library" in the traditional sense.

P#86553 2021-01-18 09:47

[Please log in to post a comment]

Follow Lexaloffle:        
Generated 2021-07-27 22:07:09 | 0.036s | Q:15