I'm iterating over t like this
double dt = 0.1, t;
double tmax = 100;
for (t = 0; t <= tmax; t += dt) { /*do something*/ }
If dt is 0.1, as here, everything works as it should and the step is executed for t = 100. But if I decrease the step, for example dt = 0.001, the last step is not executed.
How should I properly iterate over doubles?
Iterate over an integer and use a start-step formula to get each double value.
double dt = 0.1, t;
double tmax = 100;
int i, n = (int)(tmax / dt); // n: check your rounding or specify explicitly.
for (i = 0; i <= n; i++) { t = 0/*start*/ + dt * i; /*do something*/ }
Your code is fine, and the behavior is expected. Ok, not expected, but explainable due to how floating point numbers work. Look here http://floating-point-gui.de/ In case the link disappears one day, google "what programmers should know about floating point" - I'm sure it'll be cached somewhere!
Instead of
for (t = 0; t <= tmax; t += dt) { /*do something*/ }
write:
double i;
for (i = 0; i <= 1000; i += 1.0) {
t = i / 10.0;
/*do something */
}
With this pattern, the loop body will be executed exactly 1001 times and each value of t will be the nearest double approximation of the number you intended (in particular, the 11th value will be 1 exactly, the 31th value will be 3 exactly, …).
Your version does not work well because simple decimal numbers such as 0.1 or 0.01 are not representable exactly in binary floating-point.
If you are willing to adapt the number of points to the fact that you are using binary floating-point, simply use a power of two and all will be well:
for (t = 0; t <= 100.0; t += 1.0 / 64.0) { /*do something*/ }
Related
My code is taking a string format and compose it into a buffer (without sprintf, itoa etc).
I'm having trouble converting a float number to string, as I need to have precision of at most 7 digits with no trailing zeros. as well as convert each number in the float variable to char (but in that matter I don't need any help).
I tried several methods, all including math calculations, but nothing has brought me to the desired outcome.
Here's my code so far, but it is messy and also sometimes gives incorrect outcome. I also believe there is a more clean and less-complicated way to do it.
any help will be widely appreciated.
if (*format == 'f') {
float f = *(float*)ptrs[counter];
char str[30];
int b, c, m, k = 7, i = 0, l = 0;
int a = f;
f -= a;
while (k >= 0) {
l = pow(10, k);
m = a / l;
if (m > 0) {
break;
}
k--;
}
printf("%d", k);
for (l = k + 1; l > 0; l--) {
b = pow(10, l - 1);
c = a / b;
str[i++] = c + 48;
a %= b;
}
str[i++] = '.';
for (l = 0; l < 7; l++) {
f *= 10.0;
b = f;
str[i++] = b + 48;
f -= b;
}
for (i = 0; i < 7; i++) {
*buffer = str[i];
buffer++;
}
counter++;
str[i] = '\0';
for example:
input: float v2 =0.9372;
output: .937199
desired output: 0.9372
input: float v2 =0.25000;
output: 1.25000
desired output: 0.25
he's messy and also sometimes gives incorrect outcome
At some point given the base 2 nature of typical floating point, the programmer needs to make choices:
Short code that gets close to the correct answer.
Exact correct conversion with a fair amount of code. e.g. Function to print a double - exactly
Something in between.
With common floating point encoding, there are also issues of
Infinity
Not-a-number.
Oddities like -0.0
And how portable to make the code?
Sources of inexactness
OP's use of int limits float to about [INT_MIN...INT_MAX]. Code fails for float much outside that range. Could use long long to get some more range without a great deal of code change. Better yet research float modff(float value, float *iptr).
float f = ...;
// int a=f;
long long a=f;
Repeated use of f*=10.0 below with a fractional value in f injects a possible rounding (inexactness), at each iteration.
for(l=0;l<7;l++) {
f*=10.0;
Code makes no effort to round given that f may not be 0.0 after the fraction forming for(l=0;l<7;l++) { f*=10.0; loop. I see this as a place to improve precision. This area is also tricky as the round-up may effect many leading digits when they are 9, eventually needing a shift of the string buffer. In the 0.93721, after this loop, f was about 0.74. Since more than 0.5, a rounding would have given the answer of ".9371999" --> ".9372000".
Code aims for 7 decimal digits after the .. Values, as a decimal in code, assigned to a float match to 6 to 9 significant decimal places - which includes digits to the left of .. Expectations should not get too high.
Improvement idea
When the number of fraction digits desired is small, perform 1 scaling/round
// ASSUME f >= 0.0, (sign handling done before here)
long long a=f;
f-=a;
int int_f = round(f*pow(10, 7));
if (int_f < 10000000) {
later_printf_the_7_digits(int_f);
} else {
a++;
later_printf_the_7_digits(0);
}
I have a function that calculates the sin() of a radian angle. It takes two parameters, the value of the angle in radian and the terms. Just to make everything clear, this is how sin() is calculated:
sin(x) = x - (1/3! * X^3) + (1/5! * X^5) - (1/7! * X^7) + (1/9! * X^9) - ...
This is the function that do this calculation:
double sinx(double theta, int terms) //Theta is the angle x in radian
{
double result = 0;//this variable holds the value and it's updated with each term.
int i = 1;
int num = 3;
while(i <= terms-1)
{
if(i % 2 != 0){
result = result - ( (1.0/factorial(num)) * pow(theta, num) );
printf("if\n");//this is just for debugging
}
else if(i % 2 == 0){
result = result + ( (1.0/factorial(num)) * pow(theta, num) );
printf("else if\n");//this is for debugging too
}
printf("%lf\n", result);//debugging also
num = num + 2;
i = i + 1;
}
return theta + result; //this calculates the final term
}
The problem is the variable result's value doesn't change. Which also results in the final result not changing when using different number of terms.
Those are some outputs I get:
//with theta = 0.2 and terms = 6 ;;
if
-0.001333
else if
-0.001331
if
-0.001331
else if
-0.001331
if
-0.001331
Computed Sin<0.200000> = 0.198669. //this is the returned value. It's printed in the main
//with theta = 0.2 and terms = 7
if
-0.001333
else if
-0.001331
if
-0.001331
else if
-0.001331
if
-0.001331
else if
-0.001331
Computed Sin<0.200000> = 0.198669.
Any ideas?
Your code should be totally right. At least my calculator gives the same result.
If you change your printf("%lf\n", result); to printf("%.17f\n", result); you get this output:
if
-0.00133333333333333
else if
-0.00133066666666667
if
-0.00133066920634921
else if
-0.00133066920493827
if
-0.00133066920493878
else if
-0.00133066920493878
Now you can see, that it is still changing in every loop, but very little.
Really it converges fast so for double precision there is no difference between 6 and 7 terms. Here is the dump with better precision:
if
-0.00133333333333333350
else if
-0.00133066666666666680
if
-0.00133066920634920640
else if
-0.00133066920493827170
if
-0.00133066920493878470
Sin(0.2, 6) = 0.19866933079506122000
if
-0.00133333333333333350
else if
-0.00133066666666666680
if
-0.00133066920634920640
else if
-0.00133066920493827170
if
-0.00133066920493878470
else if
-0.00133066920493878450
Sin(0.2, 7) = 0.19866933079506122000
Everything looks correct here. The reason the result doesn't appear to change is to do with how quickly the Taylor series for sin converges for small angles. If you try with a bigger number say pi you should see the value updating slightly more often. You may also want to include something to limit theta from -pi to +pi as sin is a periodic function.
theta = mod(theta+pi, 2*pi) - pi
Including this restriction will alleviate the need for more terms if you start calculating values > pi or < -pi
If performance is important then you can reduce some of the calculations by removing repeats in calculating the factorials and large exponents
double sin(double theta, int terms = 7)
{
theta = mod(theta+pi, 2*pi) - pi;
double sum = x, term = x, fact = 3;
for (int i = 1; i < terms; i++)
{
term = -term * theta * theta /(fact * (fact - 1));
sum += term;
fact += 2;
}
return sum;
}
Your program as you have posted seems quite right IF you have your factorial written the right way. I have written factorial this way:
double factorial(int n) {
if(n <= 1) {
return 1.0;
}
else {
return n * factorial(n-1);
}
}
Try using this.
Using 0.785398 (approx. pi/4) and 10 terms, I get output 0.707107.
double d = sinx(0.785398, 10);
printf("%f\n", d); // prints 0.707107
Here are some runs:
printf("%.20f\n", sinx(3.1415926535897932, 100));
printf("%.20f\n", sinx(3.1415926535897932/2, 100));
printf("%.20f\n", sinx(3.1415926535897932/4, 100));
Outputs:
0.00000000000000044409
1.00000000000000000000
0.70710678118654746000
which seem accurate enough, given the pi used is only approximate.
What did you expect ?
The third term is
0.2^5/120 = 0.000002
if you show the first six decimals, and the next terms are yet smaller.
Side remark:
It is much more efficient and more accurate to compute a term from the previous, using the recurrence
T*= Z/(N*(N-1))
where Z= -X*X (and this way, the alternating signs are automatically handled).
I have a logical problem in my code, maybe it is caused by overflowing but I can't solve this on my own, so I would be thankful if anyone can help me.
In the following piece of code, I have implemented the function taylor_log(), which can count "n" iterations of taylor polynomial. In the void function I am looking for number of iterations (*limit) which is enough to count a logarithm with desired accuracy compared to log function from .
The thing is that sometimes UINT_MAX is not enough iterations to get the desired accuracy and at this point I want to let the user know that the number of needed iterations is higher than UINT_MAX. But my code don't work, for example for x = 1e+280, eps = 623. It just counts, counts and never give result.
TaylorPolynomial
double taylor_log(double x, unsigned int n){
double f_sum = 1.0;
double sum = 0.0;
for (unsigned int i = 1; i <= n; i++)
{
f_sum *= (x - 1) / x;
sum += f_sum / i;
}
return sum;
}
void guessIt(double x, double eps, unsigned int *limit){
*limit = 10;
double real_log = log(x);
double t_log = taylor_log(x, *limit);
while(myabs(real_log - t_log) > eps)
{
if (*limit == UINT_MAX)
{
*limit = 0;
break;
}
if (*limit >= UINT_MAX/2)
{
*limit = UINT_MAX;
t_log = taylor_log(x, *limit);
}
else
{
*limit = (*limit) *2;
t_log = taylor_log(x, *limit);
}
}
}
EDIT: Ok guys, thanks for your reactions so far. I have changed my code to this:
if (*limit == UINT_MAX-1)
{
*limit = 0;
break;
}
if (*limit >= UINT_MAX/2)
{
*limit = UINT_MAX-1;
t_log = taylor_log(x, *limit);
}
but it still doesn't work correctly, I have set printf to the beggining of taylor_log() function to see the value of "n" and its (..., 671088640, 1342177280, 2684354560, 5, 4, 3, 2, 2, 1, 2013265920, ...). Don't understand it..
This code below assigns the limit to UINT_MAX
if (*limit >= UINT_MAX/2)
{
*limit = UINT_MAX;
t_log = taylor_log(x, *limit);
}
And your for loop is defined like this:
for (unsigned int i = 1; i <= n; i++)
i will ALWAYS be less than or equal to UINT_MAX because there is never going to be a value of i that is greater than UINT_MAX. Because that's the largest value i could ever be. So there is certainly overflow and your loop exit condition is never met. i rolls over to zero and the process repeats indefinitely.
You should change your loop condition to i < n or change your limit to UINT_MAX - 1.
[Edit]
OP coded correctly but must insure a limited range (0.5 < x < 2.0 ?)
Below is a code version that self determines when to stop. Iteration count goes high near x near 0.5 and 2.0. The iteration count needed goes into the millions. Such the alternative coded far below.
double taylor_logA(double x) {
double f_sum = 1.0;
double sum = 0.0;
for (unsigned int i = 1; ; i++) {
f_sum *= (x - 1) / x;
double sum_before = sum;
sum += f_sum / i;
if (sum_before == sum) {
printf("%d\n", i);
break;
}
}
return sum;
}
Wrongalternative implementation of the series: Ref
Sample alternative - it converges faster.
double taylor_log2(double x, unsigned int n) {
double f_sum = 1.0;
double sum = 0.0;
for (unsigned int i = 1; i <= n; i++) {
f_sum *= (x - 1) / 1; // / 1 (or remove)
if (i & 1) sum += f_sum / i;
else sum -= f_sum / i; // subtract even terms
}
return sum;
}
A reasonable number of terms will converge as needed.
Alternatively, continue until terms are too small (maybe 50 or so)
double taylor_log3(double x) {
double f_sum = 1.0;
double sum = 0.0;
for (unsigned int i = 1; ; i++) {
double sum_before = sum;
f_sum *= x - 1;
if (i & 1) sum += f_sum / i;
else sum -= f_sum / i;
if (sum_before == sum) {
printf("%d\n", i);
break;
}
}
return sum;
}
Other improvements possible. example see More efficient series
First, using std::numeric_limits<unsigned int>::max() will make your code more c++-ish than c-ish. Second, you can use the integral type unsigned long long and std::numeric_limits<unsigned long long>::max() for the limit, which is pretty mush the limit for an integral type. If you want a higher limit, you may use long double. floating points also allows you to use infinity with std::numeric_limits<double>::infinity() note that infinity work with double, float and long double.
If neither of these types provide you the precision you need, look at boost::multiprecision
First of all, the Taylor series for the logarithm function only converges for values of 0 < x < 2, so it's quite possible that the eps precision is never hit.
Secondly, are you sure that it loops forever, instead of hitting the *limit >= UINT_MAX/2 after a very long time?
OP is using the series well outside its usable range of 0.5 x < 2.0 with calls like taylor_log(1e280, n)
Even within the range, x values near the limits of 0.5 and 2.0 converge very slowly needing millions+ of iterations. A precise log() will not result. Best to use the 2x range about 1.0.
Create a wrapper function to call the original function in its sweet range of sqrt(2)/2 < x < sqrt(2). Converges, worst case, with about 40 iterations.
#define SQRT_0_5 0.70710678118654752440084436210485
#define LN2 0.69314718055994530941723212145818
// Valid over the range (0...DBL_MAX]
double taylor_logB(double x, unsigned int n) {
int expo;
double signif = frexp(x, &expo);
if (signif < SQRT_0_5) {
signif *= 2;
expo--;
}
double y = taylor_log(signif,n);
y += expo*LN2;
return y;
}
I created this function CalculateCos:
int Factorial (long int n)
{
long int r = 1;
for (int i = 2; i<=n; i++)
{
r = r*i;
}
return r;
}
float CalculateVariable(int CVnumber, int CVloopCounter)
{
float CVresult = 0;
CVresult = pow(CVnumber, (CVloopCounter*2)) / (long int)Factorial(CVnumber*2);
return CVresult;
}
float CalculateCos(int number)
{
float result = 1;
int loopCounter = 1;
int minusOrPlus = 1;
while(loopCounter <= precision && loopCounter <= 8)
{
if(!minusOrPlus)
{
result = result - CalculateVariable(number, loopCounter);
printf("%f\n", result);
minusOrPlus = 1;
}
else
{
result = result + CalculateVariable(number, loopCounter);
printf("%f\n", result);
minusOrPlus = 0;
}
loopCounter++;
}
return result;
}
The reason why I printf after the subtraction or adding, is because it gives me strange output, like:
Enter a number, for the cos function
6
1.000000
0.999997
1.000095
0.996588
1.122822
-3.421593
160.177368
-5729.385254
Result is: -5729.3852539
Official function result is: 0.9601703
Can you help me to get correct results on this?
UPDATE:
Now my solution is:
float CalculateCos(float number)
{
float result = 0;
float step = 1;
int loopCounter = 1;
while(loopCounter <= 5)
{
step = step * (-number) * number / (((2*loopCounter)-1)*((2*loopCounter)-2));
result += step;
loopCounter++;
}
return result;
}
Current problem:
since your Factorial function returns int and you casts it to long int, its result is going to overflow even before the input goes to 16 in your case (14! > max_int).
You're calculating cos using Taylor series:
cos(x) = 1 - x2/2! + x4/4! - x6/6!
+ ...
I'm not going to write code. But there are some things wrong in your program, which can be fixed easily:
The input is in radian, so number should be a float.
Calculating each step of Taylor series using exponentiation and factorial separately leads to overflow very soon. The correct way is maintaining a float variable: step = 1 at first and in kth loop iteration step = step * (- x) * x / ((2*k-1)*(2*k)). In this way, you simply add step to result in the loop and don't need minusOrPlus anymore.
The number of loop iterations is bounded by 8 which is too small, so the result could be not precise enough.
I don't see you use precision variable anywhere. It could be used to check precision of the result. For example, when abs(step) < precision, we're going to terminate the loop.
I'm really confused on this code.
Here's what I want it to do: Start with a "v" value of 5, carry out the rest of the functions/calculations, increase the "v" value by 0.01, carry out the functions/calculations, then increase the "v" value by 0.01 again, carry out the functions...do this 500 times or until a "v" value of 10.00 is reached, whichever is easier to code.
Here is my code at the moment:
//start loop over v
for(iv=5;iv<=500;iv++) {
v=0.01*iv;
//Lots and lots of calculations with v here
}
Here is what I get: I tried setting iv<=10 so it does 10 loops only just so I could test it first before leaving it on all night. It did only 6 loops, starting at v=0.05 and ending at 0.1. So the problem is that a) it didn't run for 10 loops, b) it didn't start at 5.00, it started at 0.05.
Any help would be appreciated.
EDIT: Holy crap, so many answers! I've tried 2 different answers so far, both work! I've been staring at this and changing code around for 3 hours, can't believe it was so easy.
You need to start at iv = 500. and if you want 10 loops, and iv++ is the update, then you stop before 510.
Reason: v = 0.01*iv, so v = 5 means iv = 5/0.01 = 500. As for the number of iterations, if your for loop is of the form for (x = N; x < M; x++) (constant N and M), then max(0, M-N) loops are executed, if x is not changed in the loop and no weird stuff (e.g. overflow, hidden casts of negative numbers to unsigned, etc.) occurs.
EDIT
Instead of using v = 0.01 * iv, v = iv / 100.0 is probably more accurate. Reason: 0.01 is not exactly representable in floating point, but 100.0 is.
Changing SiegeX's code so it uses integers ("more accurate"):
double dv;
int iv;
for(iv = 500; dv <= 1000; iv += 1)
{
dv = (double)iv / 100.0;
}
double iv;
for(iv = 5.0; iv <= 10.0 ; iv += 0.01) {
/* stuff here */
}
int i;
double v;
v = 5;
for (i = 0; i < 500; i++)
{
v += 0.01;
// Do Calculations Here.
if (v >= 10.00) break;
}
This gives you both. This will iterate at most 500 times, but will break out of that loop if the v value reaches (or exceeds) 10.00.
If you wanted only one or the other:
The 10.00 Version:
double v;
v = 5.0;
while ( v < 10.00 )
{
v += 0.01;
// Do Calculations Here.
}
The 500 iterations version:
double v;
int i;
v = 5.0;
for( i = 0; i < 500; i++ )
{
v += 0.01;
// Do Calculations.
}
(Note that this isn't C99, which allows for a cleaner declaration syntax in the loops).
iv <= 10 doesn't do it for 10 loops, it does it until iv is greater than 10.
//start loop over v
for(iv=0;iv<500;iv++) //loop from 0 to 499
{
v=v+0.01; //increase v by 0.01
//Lots and lots of calculations with v here
}
this should do it