floats from Vector3 seem to work differently to other floats, or am I doing it wrong?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By af7567

I know that floating point number precision can be a bit off, but I am confused why a float from a Vector3 would be any different from a standard float. I was trying to work out why my 1 became a 0, and my 12 became 11 when converted from float to int and after a bit of playing around I found that the problem was caused by Vector3.
The following code demonstrates the problem:

func _ready():
    # Wrong result:
    var test_point = Vector3(3.8, 3, 7.5)
    var i = test_point.x / 3.8
    print("i as float: ", i)
    var int_i = int(i)
    print("i as int: ", int_i)
    
    # Right result:
    var j: float
    j = 3.8
    j = j / 3.8
    print("j as float: ", j)
    var int_j = int(j)
    print("j as int: ", int_j)

I get the following output:

i as float: 1
i as int: 0
j as float: 1
j as int: 1

I am using Godot 3.3.2.stable on Linux 64-bit

Super strange behavior. I think this belongs as a github issue. Mathematically Vector3(x,y,z) / n = Vector3(x/n, y/n, z/y) therefore Vector3.x / n = x / n so unless otherwise noted in the docs the order in which you perform operations shouldn’t matter.

In your case doing int(Vector3.x / float) == 0 while doing int((Vector3 / float).x) == 1.

As a work around you can do var i = (test_point / 3.8).x to get the results you desire.

timothybrentwood | 2021-08-19 02:54

This is not bug.
You’re getting such results of “unspecified behavior” because of combination of optimization, float precision specifics, and unspecified (by you) type-conversion operation.

Just change line #04 to either of

var i  = test_point.x / test_point.x
var i  = round(test_point.x / 3.8)
var i  = ceil(test_point.x / 3.8)

And your “wrong” part will become “right” one.

sash-rc | 2021-08-19 10:27

“unspecified (by you) type-conversion operation.” That’s the problem. Do the same operations on both, and everywhere in the editor you can and see they’re both assigned to the value 1 (printing them, using the remote scene tree). Yet for some reason as an end user using int() on one of the 1s and int() on the other 1 results in two different behaviors.

var some_float = 3.8
var v = Vector2(some_float, some_float)
var f1 = float(v.x / some_float)
var f2 = float((v / some_float).x)
var f3 = float(some_float / some_float)
printt(f1, f2, f3)
printt(int(f1), int(f2), int(f3))

If you can explain to me as an end user, not going into the black box of the internals, why you would get 2 of the results from int() the same and one different for the above code then I would love to hear it. I don’t think ‘floating point precision’ is a good enough answer when both f1 and f2 result in 1 when printing them and looking at the remote scene tree. I would agree if you could see the binary representation of them somewhere using the editor but to my knowledge you can’t.

timothybrentwood | 2021-08-19 12:25

you could see the binary representation of them somewhere using the editor but to my knowledge you can’t.

Of course you can, add print("%.20f " % f1, "%.20f " % f2, "%.20f" % f3) and you’ll see:

0.99999998745165370000 1.00000000000000000000 1.00000000000000000000

You can use more decimals in format / print, like print("%.20f" % value):

The float numbers are named like this because they are never (guaranteed to be always) precise, nor consistent across platforms like integers.
But what is more important, you should explicitly control type conversions wherever it matters, like use ceil(), round(), stepify() etc. instead of just int()

sash-rc | 2021-08-19 13:09

:bust_in_silhouette: Reply From: klaas

Hi
godot uses 64 bit floats as float but 32 bit floats in the vector type.
Since every float has finite precision you have a very slight difference between this two numbers. Then casting to int() is not a rounding, its truncating, so a 0.9999999999999 would be 0 after casting. You can use round() to get more consistent integer casting.

# Wrong result:
var test_point = Vector3(3.8, 3.8, 7.5)
var i = test_point.x / 3.8
print("i as float: ", i)
var int_i = int(i)
print("i as int: ", int_i)

# Right result:
var j: float
j = 3.8
j = j / 3.8
print("j as float: ", j)
var int_j = int(j)
print("j as int: ", int_j)	

# also Right result:
var k = test_point.x / test_point.y
print("k as float: ", k)
var int_k = int(k)
print("k as int: ", int_k)

Read more about the imprecission here:
https://forum.godotengine.org/31332/turning-floats-into-vectors-results-in-added-decimals