3
$\begingroup$

Attached is a Geometry nodes timer in days, hours, mins, seconds. The clock is... nothing special. It takes the days, hours, minutes seconds input, evaluates everything into seconds, adds the frame time, then re-composes the time into a dynamic string.

geometry nodes calendar

If you enter 192 days in the Group input and run the animation, the clock turns over as you'd expect.

If you enter 193 days or over, things get weird.

The maximum value of seconds is 31 million - easily handled by floats or integers. But the value doesn't increment regularly if days exceed 193 or so.

Any help?

$\endgroup$
3
  • $\begingroup$ just a first test, when I enter 0d 0h 1m 55s at frame 0 then the result is 0d 1h 1m 55s. This is +1 hour and doesn't seem right. $\endgroup$
    – Blunder
    Commented Jun 5, 2023 at 9:38
  • $\begingroup$ Ugh. Thanks. Fortunately, that should be a Math node error I can fix. The anomalous animation numbers going past 193-4 days... I don't understand that bit. $\endgroup$
    – OroNZ
    Commented Jun 5, 2023 at 11:14
  • $\begingroup$ Remove the floor math node in the days part $\endgroup$
    – Gorgious
    Commented Jun 6, 2023 at 9:31

1 Answer 1

8
$\begingroup$

Geometry Nodes floats are implemented as IEEE 754 float32(1). This means the mantissa part has 23 bits. Mantissa encodes a value between 1 and 2. This means that $\text{mantissa}^{exponent}$ can use an exponent of 23 (it's not a coincidence it's the same number as the number of bits of mantissa¹) to encode the smallest value $8388608.0$: 01001011000000000000000000000000

and the biggest value $16777215.0$: 01001011011111111111111111111111

Then for the next number you need to increase the exponent and start at mantissa = 0 (implied +1):

01001011100000000000000000000000

Problem is, at this point you can't increase the number by 1 again, because you ran out of the float precision: the smallest possible change to mantissa is ${1\over2^{23}} = 0.00000011920928955078125$:

$$1 × 2^{24} = 1 × 16 777 216$$ $$1.00000011920928955078125 × 2^{24} = 16777218$$

For the above screenshots I used a great tool to play with floats32:

6 h-schmidt.net: IEEE-754 Floating Point Converter

Here's a Q&A about C#, but it implements the same IEEE 754 standard as C/C++ used by Blender:

8 Why does a float variable stop incrementing at 16777216 in C#?

Geometry Nodes context

The problem in regards to geonodes comes down to the fact there are no nodes for mathematical operations on integers. So whenever you do stuff like modulo, you need to connect an integer to a float socket, which converts the variable and loses precision in the process. I ran into this problem here:

10 How can I re-sort the points/indexes of an object in geometry nodes?

If you read into the comments you will see how Quellenform, upon testing, discovered the setup starts failing at large geometries, and I later discovered it starts failing after exactly $4096$ vertices: that's because I square my geometry there, and $4096^2 = 16777216$, Once you exceed that number, doing divmod to get the row/column of a squared geometry starts failing. In my case the solution was easy, nowadays when I square geometries I do it like so:

12

In your case it's not immediately obvious what the algorithm should be, except that you shouldn't add all the days, hours, minutes, seconds together to then modulo them; you want to avoid big numbers, so if you e.g. want to be able to input a number of days with a fraction part (I imagine that's why the socket types are floats), then take the fraction part, multiply by the 24 and add to hours, then take the fraction part of that, multiply by 60 and add to minutes etc.

¹ - the logic is, the right-most bit of mantissa means either $0$ or $1\over 2^{23}$ to be added to fractions described by other bits (the exponent in each fraction is the number of the bit). So ${1\over 2^{23}} × 2^{23} = {2^{23}\over 2^{23}} = 1$. So for any float, once the exponent is equal to the number of (explicit) bits in mantissa, the minimum step becomes exactly $1$:

>>> import numpy as np
>>> np.nextafter(np.float16(2**10), np.float16(inf))
1025.0

>>> np.nextafter(np.float32(2**23), np.float32(inf))
8388609.0

>>> nextafter(2**52, inf)
4503599627370497.0

(the mantissa in float16 has 10 explicit bits, and in float64 used by Python has 52 explicit bits)

For some interesting bugs in integer domain, related to the fact underlying implementation uses floats32, see this:

Geometry Nodes: How to Read Last Digit of a Big Integer?

$\endgroup$
7
  • $\begingroup$ I was hoping it wasn't going to be a binary space problem :( I could avoid big numbers completely and turn my nice, easy-to-read Math nodes into a bunch of complicated Compare/Switch node conditionals, to cater for the 60s/60m/24h/365d rollovers to zero. That's not fun. But thanks, Markus :) $\endgroup$
    – OroNZ
    Commented Jun 5, 2023 at 11:37
  • $\begingroup$ A more careful read of your explanation raises another question around Modulo arithmetic. How does connecting an int to a float input 'lose precision'? I can see that practice gaining decimal precision for the operation, but not losing its pre-decimal magnitude. Is precision loss always the case, or are you talking about the magnitude of the int and float number spaces? $\endgroup$
    – OroNZ
    Commented Jun 5, 2023 at 23:15
  • $\begingroup$ @OroNZ both integer and float use 32 bits of memory. It would be silly if a float could express all possible integers, and then on top of that some non-integers :D If you draw a histogram with the bucket width of 1, the line describing how many numbers can be stored for an integer will be a horizontal line from -2147483648 to +2147483647. If you draw a float, it will by a logarithmic distribution, with a very high peak of [0..1) buckets of height $(2^7-1)×2^{23}+1$, then in range [1..2) the height will be $2^{23}$, two buckets in [2..4) will share $2^{23}$, so ${2^{23}\over2}=2^{22}$ each etc. $\endgroup$ Commented Jun 6, 2023 at 7:53
  • $\begingroup$ Eventually the ranges will spread there's more buckets to share a range than the numbers in that range, and at that point only every 2nd bucket gets a single number (and then only every 4th bucket etc.) $\endgroup$ Commented Jun 6, 2023 at 8:36
  • $\begingroup$ Thanks again, Markus :) I've modified my clock. hh:mm:ss are still worked out the same way, but days are no longer converted into secs - a Compare-Switch pair increments days if hours goes over 23. $\endgroup$
    – OroNZ
    Commented Jun 6, 2023 at 9:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .