finding variable in array with decimal places not working as expected

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

Hello and thanks for looking! This works as expected:

var TimeArray = [1.01, 2.01, 3.01, 4.01]
var Ticks = 0.01
var Place = 0

func _on_Timer_timeout(): # (once per second)
	Ticks += 1
	print(Ticks)
	Place = TimeArray.find(Ticks)
	print('position in array = ' + str(Place))

output:

1.01
position in array = 0
2.01
position in array = 1
3.01
position in array = 2
4.01
position in array = 3
5.01
position in array = -1

This does not work as expected:

var TimeArray = [1, 2, 3, 4]
var Ticks = 0
var Place = 0

func _on_Timer_timeout(): # (once per 0.1 seconds)
	Ticks += .1
	print(Ticks)
	Place = TimeArray.find(Ticks)
	print('position in array = ' + str(Place))

output:

0.1
position in array = -1
0.2
position in array = -1
0.3
position in array = -1
0.4
position in array = -1
0.5
position in array = -1
0.6
position in array = -1
0.7
position in array = -1
0.8
position in array = -1
0.9
position in array = -1
1
position in array = -1
1.1

I expect that when Ticks value reaches 1 I would get “position in array = 0” instead of -1

Been at this for days Pls Halp. Thanks!

(background for issue: NPC needs to perform actions at given times, times come from array that will change regularly)

:bust_in_silhouette: Reply From: jgodfrey

This is a classic floating point error issue. That is, your running Ticks value is likely have some very small, floating point error in it, so it’s never exactly equal to the value you think it is.

Bottom line, trying to match some accumulating, floating point value to some pre-recorded values in an array is a recipe for disaster.

Ignoring the issue at hand, what are you really trying to do here? That is, if you could get the array lookup to work as you want, what does that buy you? What’s the end-game?

Additionally, I’d recommend that you print your running value with more precision - you’ll likely see the error I’m talking about. So, change that print to something like:

print("%.14f" % Ticks)

jgodfrey | 2020-12-13 20:14

HI and thank you for your answer.

What seems odd to me is that it will print the Tick value fine, but doesn’t seem to recognize it as a variable?

I’m making a talking NPC. His mouth will move in sync with the sounds. The mouth shapes and times are in arrays. The times are rounded to .01 seconds by the software that provides the array (‘Rhubarb’ fyi)

Based on the idea that it could be a floating point error, I tried this. Unexpected result, it found the first value but not the second, even though the variable as printed seems correct.

var TimeArray = [1.01, 2.01, 3.01, 4.01]
var Ticks = 0.01
var Place = 0

func _on_Timer_timeout():
	Ticks += .1
	print(Ticks)
	Place = TimeArray.find(Ticks)
	print('position in array = ' + str(Place))

output:

0.11
position in array = -1
0.21
position in array = -1
0.31
position in array = -1
0.41
position in array = -1
0.51
position in array = -1
0.61
position in array = -1
0.71
position in array = -1
0.81
position in array = -1
0.91
position in array = -1
1.01
position in array = 0   =============> FOUND IT
1.11
position in array = -1
1.21
position in array = -1
1.31
position in array = -1
1.41
position in array = -1
1.51
position in array = -1
1.61
position in array = -1
1.71
position in array = -1
1.81
position in array = -1
1.91
position in array = -1
2.01
position in array = -1  =============> DID NOT FIND IT
2.11
position in array = -1

arpeggiocat | 2020-12-13 20:48

Additionally, I’d recommend that you print your running value with
more precision - you’ll likely see the error I’m talking about. So,
change that print to something like:

print(“%.14f” % Ticks)

Is that 14 decimal places? I am using 2. If I wanted to limit my values to 2 places, would this do the trick? I couldn’t catch any values from the array :confused:

func _on_Timer_timeout():
	Ticks += .1
	Ticks_b = "%.2f" % Ticks
	print(str(Ticks_b))
	Place = TimeArray.find(Ticks_b)
	print('position in array = ' + str(Place))

arpeggiocat | 2020-12-13 20:50

Yes, that’s 14 places (14 isn’t magical, just a good number of decimal places). And, yeah, do that.

The point is, I think the value that you think is 2.01 (for example), is probably something like 2.0100000001 or 2.009999999999 - so not exactly the same value.

By printing more decimal places, I think you’ll see the reason for the problem.

jgodfrey | 2020-12-13 20:58

Assuming this is the problem, you could try to force your Tick variable to 2 decimal places before making the array comparison, but it still sounds potentially dangerous to me…

var val = stepify(Ticks, 0.01)

That’ll round Ticks to 2 decimal places and store the result in val. You might try seeing if you can consistently find that value in your array…

jgodfrey | 2020-12-13 21:03

One other idea. Rather than just asking the array if it has one of the values you’re interested in you could iterate over the array yourself and find a value that’s close enough to what you want.

You could make that comparison via the is_equal_approx(a, b) function.

See here:

@GDScript — Godot Engine (stable) documentation in English

jgodfrey | 2020-12-13 21:06

… or, you could store your timestamp array values as string representations, and then stepify() and convert your running values to strings prior to comparison. That should eliminate any floating-point issues in the comparison…

jgodfrey | 2020-12-13 21:09

Such excellent suggestions. Thank you. I spent a bit of time with Tom Scott and wow - floating points

I used two of your suggestions to get this, and it works brilliantly!

var TimeArray = [1.01, 2.01, 3.01, 4.01]
var TimeArrayString = []
var Ticks = 0
var Ticks_string = "A"
var Place = 0

func _ready():
	for i in TimeArray:
		TimeArrayString.push_back(str("%.2f" % i))
	print(TimeArrayString)

func _on_Timer_timeout():
	Ticks += .01
	Ticks_string = str("%.2f" % Ticks)
	print(str(Ticks_string))
	Place = TimeArrayString.find(Ticks_string)
	print('position in array = ' + str(Place))

You are the man!

For curiosity sake I tried multiplying the TimeArray values by 100 to in a _ready function - floating point issue persisted. Didn’t have luck with stepify but I was likely doing it wrong.

arpeggiocat | 2020-12-13 22:41