I am working on a project in BGE and I want to make an object slowly accelerate to its max speed when I press the button. When I press another button it should decelerate and then again slowly accelerate to the top speed in the other direction.
Here is the code:
import bge
cont = bge.logic.getCurrentController()
own = cont.owner
keyboard = bge.logic.keyboard
ACTIVE = bge.logic.KX_INPUT_ACTIVE
INACTIVE = bge.logic.KX_INPUT_NONE
accelerate = own['accelerateProp']
accelerateFactor = 0.005
own.applyMovement((0, accelerate, 0))
if(keyboard.events[bge.events.WKEY] == ACTIVE and accelerate > -0.05):
own['accelerateProp'] -= accelerateFactor
if(keyboard.events[bge.events.SKEY] == ACTIVE and accelerate < 0.05):
own['accelerateProp'] += accelerateFac
I have an object with the accelerateProp property which I'm using to controll the speed of the object.
When I hold W key it accelerates. But instead of stopping at -0.05, it runs one more time and it stops at -0.055. If I then hold S button, it deccelerates and then again accelerates and stops at 0.05.
The confusing thing is that if I hold S key first, it Will accelerate to 0.055, and it will work fine the other way around.
So actuali, the direction, which will reach the top speed firs will be broken and the other will be fine and it will stay that way, so one side will always have max speed 0.055 and the other 0.05.
I can't understand what's wrong. I suspect it has to do something wit game properties, but I realy don't Know. Also if there's another way of doing what I did, please let me know.
Thanks!
It's hard to be sure without more information, but I'm almost certain this is just a typical floating-point rounding error.
Consider this code (which is effectively doing the same thing as your code, but without all the user interaction stuff in the middle):
x = 0
while x < 0.05:
x += 0.005
print(x)
You'd think the result would be 0.05, right? But it's not. It's 0.05499999999999999. Why did it go one step too far?
If you print out all the values along the way, the reason is obvious:
0.005
0.01
0.015
0.02
0.025
0.030000000000000002
0.035
0.04
0.045
0.049999999999999996
0.05499999999999999
There is no floating-point double exactly equal to all of these numbers, so you accumulate rounding error. When you get to the 10th one, it's not 0.05, it's 0.049999999999999996, which is still less than 0.05, so it goes one more step.
There are two solutions to problems like this.
First, you can use Decimal instead of float. Of course Decimal is just as inexact and subject to rounding errors as float in general; the difference is that any number that has an exact decimal string representation (like 0.005 and all of your other values) also has an exact Decimal representation. So:
from decimal import Decimal
x = Decimal('0')
while x < Decimal('0.05'):
x += Decimal('0.005')
print(x)
And now you get 0.05 instead of 0.05499999999999999.
Alternatively, you can use absolute or relative epsilon comparisons. For example:
eps = 1e-5
while x + eps < 0.05:
x += 0.005
print(x)
Now you get 0.049999999999999996 instead of 0.05499999999999999 at the end.
The reason for the problems are rounding-errors with floating-points. Unfortunately, for floating-points addition and multiplication introduce small errors that can lead to problems with comparisons.
Instead, you should use "clamping", which means you prevent over/undershooting by comparing with a threshold, and then setting the value to the threshold itself:
acceleration = -0.05 if braking else 0.05
speed = max(0, min(max_speed, speed + acceleration))
Thus speed will never exceed the boundaries of [0..max_speed]
round off your property. prop = round(prop["value"], 2)
The 2 is the round point.
Related
I have a series of data points which form a curve I do not have an equation for, and for which i have not been able to satisfyingly calculate an equation with either libreoffice or the online curve fitting tools in the first 2 pages of google results.
I would like the equation for the curve and ideally a python implementation of calculating y values for a given x value along that curve in case there are unexpected hoops to jump through. Failing that I would like any more elegant python solution than a list of elif statements incrementing y if x is high enough for it to increase by a whole number, which is the ugly solution of last resort - my immediate plans do not require decimal precision.
The curve crosses the zero line at 10, and every whole number incrementation of y requires x to be incremented by one more whole number than the previous, so y1 is reached at x11, y2 at x13, y3 at x16 etc, with the curve bending in the other direction in the negatives such that y-1 is at x9, y-2 is at x7 etc. I suspect i am missing something obvious as far as finding the curve equation when i already have this knowledge.
In addition to trying to use libreoffice calc and several online curve-fitting websites to no avail, i have tried slicing the s-curve (I have given up on searching the term sigmoid function as all my results are either related to neural nets or expect my y values to never exceed +-1) into two logarythmic curves, which almost works - 5 *(np.log(x) - 11) gets something frustratingly close to the top half of the curve, but which i ultimately haven't been able to use - in addition to crossing the number line at 9 it produced some odd behaviour when I returned round() rounded y values directly, displaying results in the negative 40s when returned directly, but seeming to work fine when those numbers are fed into other calculations.
If somebody can give me two working logarythms that round to the right numbers for x values between 0 and 50 that is good enough for this project.
Thank you for your time and patience.
-EDIT-
these are triangular numbers apparently, x-10 is equal to the number of dots in a triangle with y dots on each side, what I need is the inverse of the triangular number formula. Thank you to everyone who commented.
As mentioned in my edit, the y i am trying to find is the triangular root of x. This solution:
def get_triangle_root(x: int) -> int:
current_value = x - 10
negative = False
if current_value < 0:
current_value = current_value * -1
negative = True
current_value = np.sqrt(1 + (current_value * 8))
current_value = (current_value - 1)/2
if negative == True:
current_value = current_value * -1
current_value = int(current_value)
return current_value
seems to work fine for now. Curiously, when I calculate (-1+(sqrt(1+(8*x)))/2) using libreoffice or google, rather than getting the same results this python script gives me, i get results 0.5 lower than the actual triangle root. Unimportant at this time, but I am curious as to what would cause it.
At any rate, thank you to everyone who lent their time to me. I apologise to anyone looking at this question who was looking for a universal solution for creating S-curves rather than just one that works for my specific task, but feel it is best to attach an answer to this question so as not to prevail on more people's time.
-EDIT- changed python script to handle negative triangular numbers as well, something i had overlooked in excitement.
What you're looking for are a class of functions called "Sigmoid functions". They have a characteristic S-shape. Go to Wolfram and play around with some common Sigmoid funcs, remembering that the "a" in a function, f(x-a), shifts the entire curve left or right, and appending a value "b" to the function, f(x-a) + b will shift the curve up and down. Using a coefficient of "c", f(c*x - a) + b here acts as a scalar. That should get you where you want to be in short time.
Example: (1/(1 + C*exp(-(x + A)))) + B
I'm having some trouble in my pygame application that I'm making. There are buttons in the menu that change how fast my simulation runs (by changing speed in clock.tick(speed*10)). This, however, also slows down the rate that the menu buttons respond. If I ingame set speed anywhere lower than 0.5 the speed and play/pause buttons take up to five seconds to register a click. So I tried creating my own way to work around this:
speed = 1 # defined previously ingame
timeSlow = 0
timeSlowGoal = 0
while True: # game loop
if speed < 1:
timeSlowGoal = reciprocal(speed) # well here's my problem
else:
timeSlowGoal = -1
if timeSlowGoal != -1:
if timeSlow >= timeSlowGoal:
timeSlow = 0
# more code that actually does stuff
else:
timeSlow += 1
# doesn't match, try again
In python, there are tricks like int(),string(), and float() to change a variable. Of course, reciprocal() doesn't work. Is there some sort of function like these that gets the reciprocal of a number, (which in this case is always a decimal) or do I need to just do some fancy math to get it?
(of course if all of this is unnecessary and you have a better solution to my first problem let me know)
EDIT: Okay, there is a very simple solution to this that I overlooked. 1/n is always the reciprocal of n. There's no need for a reciprocal() function because it's so simple. That's what I get for forgetting elementary school math.
Assuming you are using Python 3, change
timeSlowGoal = reciprocal(speed)
to
timeSlowGoal = 1 / speed
Just make sure that you don't try to do that if speed is zero.
I do not know why you are having such a huge piece of code, because Reciprocal of a number can be simply mentioned as: 1/n
So, your reciprocal function can be simply written as:
def reciprocal(n):
return 1.0 / n
Here, I am using 1.0/n instead 1/n as in python for the resultant of division to be fraction, either numerator or denominator should have fraction value.
Sample run:
>>> reciprocal(3)
0.3333333333333333
>>> reciprocal(0.3333333)
3.00000030000003 # Not '3' because of the loss in the precision of value entered
>>> reciprocal(reciprocal(3))
3.0 # Doing reciprocal twice returns "3"
I'm doing calculations with 3D vectors with floating point coordinates. Occasionally, I want to check if a vector is nonzero. However, with floating point numbers, there's always a chance of a rounding error.
Is there a standard way in Python to check if a floating point number is sufficiently close to zero? I could write abs(x) < 0.00001, but it's the hard-coded cutoff that bugs me on general grounds ...
Like Ami wrote in the comments, it depends on what you're doing. The system epsilon is good for single operation errors, but when you use already rounded values in further calculations, the errors can get much larger than the system epsilon. Take this extreme example:
import sys
print('%.20f\n' % sys.float_info.epsilon)
x = 0.1
for _ in range(25):
print('%.20f' % x)
x = 11*x - 1
With exact values, x would always be 0.1, since 11*0.1-1 is 0.1 again. But what really happens is this:
0.00000000000000022204
0.10000000000000000555
0.10000000000000008882
0.10000000000000097700
0.10000000000001074696
0.10000000000011821655
0.10000000000130038202
0.10000000001430420227
0.10000000015734622494
0.10000000173080847432
0.10000001903889321753
0.10000020942782539279
0.10000230370607932073
0.10002534076687252806
0.10027874843559780871
0.10306623279157589579
0.13372856070733485367
0.47101416778068339042
4.18115584558751685051
44.99271430146268357930
493.91985731608951937233
5432.11843047698494046926
59752.30273524683434516191
657274.33008771517779678106
7230016.63096486683934926987
79530181.94061353802680969238
Note that the original x differed from 0.1 by far less than my system epsilon, but the error quickly grew larger than that epsilon and even your 0.00001 and now it's in the millions.
This is an extreme example, though, and it's highly unlikely you'll encounter something this bad. But it shows that the precision really depends on what you're doing, so you'll have to find a good way for your particular calculation.
I have some problems due to really low numbers used with numpy. It took me several weeks to trace back my constant problems with numerical integration to the fact, that when I add up floats in a function the float64 precision gets lost. Performing the mathematically identic calculation with a product instead of a sum leads to values that are alright.
Here is a code sample and a plot of the results:
from matplotlib.pyplot import *
from numpy import vectorize, arange
import math
def func_product(x):
return math.exp(-x)/(1+math.exp(x))
def func_sum(x):
return math.exp(-x)-1/(1+math.exp(x))
#mathematically, both functions are the same
vecfunc_sum = vectorize(func_sum)
vecfunc_product = vectorize(func_product)
x = arange(0.,300.,1.)
y_sum = vecfunc_sum(x)
y_product = vecfunc_product(x)
plot(x,y_sum, 'k.-', label='sum')
plot(x,y_product,'r--',label='product')
yscale('symlog', linthreshy=1E-256)
legend(loc='lower right')
show()
As you can see, the summed values that are quite low are scattered around zero or are exactly zero while the multiplicated values are fine...
Please, could someone help/explain? Thanks a lot!
Floating point precision is pretty sensitive to addition/subtraction due to roundoff error. Eventually, 1+exp(x) gets so big that adding 1 to exp(x) gives the same thing as exp(x). In double precision that's somewhere around exp(x) == 1e16:
>>> (1e16 + 1) == (1e16)
True
>>> (1e15 + 1) == (1e15)
False
Note that math.log(1e16) is approximately 37 -- Which is roughly where things go crazy on your plot.
You can have the same problem, but on different scales:
>>> (1e-16 + 1.) == (1.)
True
>>> (1e-15 + 1.) == (1.)
False
For a vast majority of the points in your regime, your func_product is actually calculating:
exp(-x)/exp(x) == exp(-2*x)
Which is why your graph has a nice slope of -2.
Taking it to the other extreme, you're other version is calculating (at least approximately):
exp(-x) - 1./exp(x)
which is approximately
exp(-x) - exp(-x)
This is an example of catastrophic cancellation.
Let's look at the first point where the calculation goes awry, when x = 36.0
In [42]: np.exp(-x)
Out[42]: 2.3195228302435691e-16
In [43]: - 1/(1+np.exp(x))
Out[43]: -2.3195228302435691e-16
In [44]: np.exp(-x) - 1/(1+np.exp(x))
Out[44]: 0.0
The calculation using func_product does not subtract nearly equal numbers, so it avoids the catastrophic cancellation.
By the way, if you change math.exp to np.exp, you can get rid of np.vectorize (which is slow):
def func_product(x):
return np.exp(-x)/(1+np.exp(x))
def func_sum(x):
return np.exp(-x)-1/(1+np.exp(x))
y_sum = func_sum_sum(x)
y_product = func_product_product(x)
The problem is that your func_sum is numerically unstable because it involves a subtraction between two very close values.
In the calculation of func_sum(200), for example, math.exp(-200) and 1/(1+math.exp(200)) have the same value, because adding 1 to math.exp(200) has no effect, since it is outside the precision of 64-bit floating point:
math.exp(200).hex()
0x1.73f60ea79f5b9p+288
(math.exp(200) + 1).hex()
0x1.73f60ea79f5b9p+288
(1/(math.exp(200) + 1)).hex()
0x1.6061812054cfap-289
math.exp(-200).hex()
0x1.6061812054cfap-289
This explains why func_sum(200) gives zero, but what about the points that lie off the x axis? These are also caused by floating point imprecision; it occasionally happens that math.exp(-x) is not equal to 1/math.exp(x); ideally, math.exp(x) is the closest floating-point value to e^x, and 1/math.exp(x) is the closest floating-point value to the reciprocal of the floating-point number calculated by math.exp(x), not necessarily to e^-x. Indeed, math.exp(-100) and 1/(1+math.exp(100)) are very close and in fact only differ in the last unit:
math.exp(-100).hex()
0x1.a8c1f14e2af5dp-145
(1/math.exp(100)).hex()
0x1.a8c1f14e2af5cp-145
(1/(1+math.exp(100))).hex()
0x1.a8c1f14e2af5cp-145
func_sum(100).hex()
0x1.0000000000000p-197
So what you have actually calculated is the difference, if any, between math.exp(-x) and 1/math.exp(x). You can trace the line of the function math.pow(2, -52) * math.exp(-x) to see that it passes through the positive values of func_sum (recall that 52 is the size of the significand in 64-bit floating point).
i am having trouble figuring out why my timer is not reaching zero when i run it. I am trying to get the window to close when a=0 but a keeping decreasing to negative digits why please? This is my codes
def close_timer(self):
global a
a = float(a - 0.1)
self.labeltext.set(str(("%.1f" % (a))))
a = float(a)
print a
print (a == 0)
if a == 0:
self.canvas.after(100)
self.root.destroy()
self.root.after(100,self.close_timer)
Floating point number math is generally inaccurate - you can't represent a decimal 0.1 in binary floating points exactly, therefore you'll never reach 0:
>>> a = 1
>>> while a > 0:
... a = a - 0.1
... print(repr(a))
...
0.9
0.8
0.7000000000000001
0.6000000000000001
0.5000000000000001
0.40000000000000013
0.30000000000000016
0.20000000000000015
0.10000000000000014
1.3877787807814457e-16
-0.09999999999999987
Use if abs(a) < 0.000001: or something similar.
It's always dangerous to compare floats with fixed values unless you know and mitigate the consequences.
Most likely, your value is getting down to something like:
0.099999923354
as imprecision is gradually injected into your value every time you subtract 0.1 (which is not directly representable in IEEE754).
Then, when you subtract the final 0.1, you get something like:
-0.00000005345
which is not equal to zero.
Quickest fix is to probably change your zero check to be:
if a <= 0:
so that small errors are irrelevant, other than possibly taking an extra cycle (probably a tenth of a second) if it reaches 0.0000001 where it should be zero, for example.
If you want to ensure you don't take that extra cycle and you're confident that the gradually introduced error won't be too large, just change it to:
if a <= 0.0003:
or something similar.