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);
}
One of my C assignments was it to write an approximation of arctan(x) in the language C. The equation which I should base it on is
arctan(x)=\sum {k=0}^{\infty }(-1)^{k} \tfrac{x^{2k+1}}{2k+1}
In addition x is only defined as -1<=x<=1.
Here is my code.
#include <stdio.h>
#include <math.h>
double main(void) {
double x=1;
double k;
double sum;
double sum_old;
int count;
double pw(double y, double n) {
double i;
double number = 1;
for (i = 0; i < n; i++) {
number *= y;
}
return(number);
}
double fc (double y) {
double i;
double number = 1;
for (i = 1; i <= y; i++){
number *= i;
}
return(number);
}
if(x >= (-1) && x <= 1) {
for(k=0; sum!=sum_old; k++) {
sum_old = sum;
sum += pw((-1), k) * pw(x, (2*k) + 1)/((2*k) + 1);
count++;
printf("%d || %.17lf\n", count, sum);
}
printf("My result is: %.17lf\n",sum);
printf("atan(%f) is: %.17f\n", x, atan(x));
printf("My result minus atan(x) = %.17lf\n", sum - atan(x));
} else {
printf("x is not defined. Please choose an x in the intervall [-1, 1]\n");
}
return 0;
}
It seemingly works fine with every value, except value 1 and -1. If x=1, then the output ends with:
...
7207 || 0.78543285189457468
7208 || 0.78536
Whereas the output should look more like this. In this case x=0.5.
25 || 0.46364760900080587
26 || 0.46364760900080587
My result is: 0.46364760900080587
atan(0.500000) is: 0.46364760900080609
My result minus atan(x) atan(x) = -0.00000000000000022
How can I improve my code so that it can run with x=1 and x=-1.
Thanks in advance.
PS: I use my own created pw() function instead of pow(), because I wanted to bybass the restriction of not using pow() as we didn't had that in our lectures yet.
PPS: I'd appreciate any advice as to how to improve my code.
In each iteration, you add (-1)k • x2k+1 / (2k+1), and you stop when there is no change to the sum.
If this were calculated with ideal arithmetic (exact, infinitely precise arithmetic), it would never stop for non-zero x, since you are always changing the sum. When calculating with fixed-precision arithmetic, it stops when the term is so small it does not change the sum because of the limited precision.
When |x| is less than one by any significant amount, this comes quickly because x2k+1 gets smaller. When |x| is one, the term becomes just 1 / (2k+1), which gets smaller very slowly. Not until k is around 253 would the sum stop changing.
You might consider changing your stopping condition to be when sum has not changed from sum_old very much rather than when it has not changed at all.
if(x >= (-1) && x <= 1) {
for(k=0; sum!=sum_old; k++) {
sum_old = sum;
sum += pw((-1), k) * pw(x, (2*k) + 1)/((2*k) + 1);
count++;
printf("%d || %.17lf\n", count, sum);
}
Comparing doubles can be tricky. The conventional way to compare doubles is to test within epsilon. There should be an epsilon value defined somewhere, but for your purposes how many digits are enough to approximate? If you only need like 3 or 4 digits you can instead have
#define EPSILON 0.0001 //make this however precise you need to approximate.
if(x >= (-1) && x <= 1) {
for(k=0; fabs(sum - sum_old) > EPSILON; k++) {
sum_old = sum;
sum += pw((-1), k) * pw(x, (2*k) + 1)/((2*k) + 1);
count++;
printf("%d || %.17lf\n", count, sum);
}
If the issue is that -1,1 iterate too many times either reduce the precision or increase the step per iteration. I am not sure that is what you're asking though, please clarify.
I think the cause of this is for a mathematical reason rather than a programming one.
Away from the little mistakes and adjustments that you should do to your code, putting x = 1 in the infinite series of arctan, is a boundary condition:
In this series, we add a negative value to a positive value then a negative value. This means the sum will be increasing, decreasing, increasing, ... and this will make some difference each iteration. This difference will be smaller until the preciseness of double won't catch it, so the program will stop and give us the value.
But in the sum equation. When we set z = 1 and n goes from 0 to ∞, this will make this term (-1^n) equal to 1 in one time and -1 in the next iteration. Also,
the value of the z-term will be one and the denominator value when n approaches infinity will = ∞ .
So the sum several iterations will be like +1/∞ -1/∞ +1/∞ -1/∞ ... (where ∞ here represents a big number). That way the series will not reach a specific number. This is because z = 1 is a boundary in this equation. And that is causing infinite iterations in your solution without reaching a number.
If you need to calculate arctan(1) I think you should use this formula:
All formulas are from this Wikipedia article.
Here is some modifications that make your code more compact and has less errors:
#include <stdio.h>
#include <math.h>
#define x 0.5 //here x is much easier to change
double pw(double, double); //declaration of the function should be done
int main() { //the default return type of main is int.
double k;
double sum = 0 ; //you should initiate your variables.
double sum_old = 1 ; //=1 only to pass the for condition first time.
//you don't need to define counter here
if(x < -1 || x > 1){
printf("x is not defined. Please choose an x in the interval [-1, 1]\n");
return 0;
}
for(k=0; sum!=sum_old; k++) {
sum_old = sum;
sum += pw((-1), k) * pw(x, (2*k) + 1)/((2*k) + 1);
printf("%.0f || %.17lf\n", k, sum);
}
printf("My result is: %.17lf\n",sum);
printf("atan(%f) is: %.17f\n", x, atan(x));
printf("My result minus atan(x) = %.17lf\n", sum - atan(x));
return 0;
}
double pw(double y, double n) { //functions should be declared out of the main function
double i;
double number = 1;
for (i = 0; i < n; i++) {
number *= y;
}
return(number);
}
double fc (double y) {
double i;
double number = 1;
for (i = 1; i <= y; i++){
number *= i;
}
return(number);
}
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'm trying to use the Sine Table lookup method to find the tone frequency at different step size, but when I'm converting the floating point to integer and use the oscicopte to view the frequncy, it can't display any things on screen.
Does anyone know what's the solution for this issues. Any help is apperaite.
Below is the code:
// use the formula: StepSize = 360/(Fs/f) Where Fs is the Sample frequency 44.1 kHz and f is the tone frequency.
// example: StepSize = 360/(44100/440) = 3.576, since the STM32 doesn't support the floating point, therefore, we have to use the fixed-point format which multiply it by 1000 to be 3575
int StepSize = 3575;
unsigned int v=0;
signed int sine_table[91] = {
0x800,0x823,0x847,0x86b,
0x88e,0x8b2,0x8d6,0x8f9,
0x91d,0x940,0x963,0x986,
0x9a9,0x9cc,0x9ef,0xa12,
0xa34,0xa56,0xa78,0xa9a,
0xabc,0xadd,0xaff,0xb20,
0xb40,0xb61,0xb81,0xba1,
0xbc1,0xbe0,0xc00,0xc1e,
0xc3d,0xc5b,0xc79,0xc96,
0xcb3,0xcd0,0xcec,0xd08,
0xd24,0xd3f,0xd5a,0xd74,
0xd8e,0xda8,0xdc1,0xdd9,
0xdf1,0xe09,0xe20,0xe37,
0xe4d,0xe63,0xe78,0xe8d,
0xea1,0xeb5,0xec8,0xedb,
0xeed,0xeff,0xf10,0xf20,
0xf30,0xf40,0xf4e,0xf5d,
0xf6a,0xf77,0xf84,0xf90,
0xf9b,0xfa6,0xfb0,0xfba,
0xfc3,0xfcb,0xfd3,0xfda,
0xfe0,0xfe6,0xfec,0xff0,
0xff4,0xff8,0xffb,0xffd,
0xffe,0xfff,0xfff};
unsigned int sin(int x){
x = x % 360;
if(x <= 90)
return sine_table[x];
else if ( x <= 180){
return sine_table[180 - x];
}else if ( x <= 270){
return 4096 - sine_table[x - 180];
}else{
return 4096 - sine_table[360 - x];
}
}
void main(void)
{
while(1){
v+=StepSize; // Don't know why it doesn't work in this way. not display anything on screen.
DAC->DHR12R2 = sin(v/1000); // DAC channel-2 12-bit Right aligned data
if (v >= 360) v = 0;
}
}
But, if I change the StepSize = 3; it shows the frequency:
There are a few issues with your code. But I will start with the one that you asked about.
int StepSize = 3575;
unsigned int v=0;
while(1){
v+=StepSize;
DAC->DHR12R2 = sin(v/1000);
if (v >= 360) v = 0;
}
The reason why this code doesn't work is that v is always set to 0 at the end of the loop because 3575 is greater than 360. So then you always call sin(3) because 3575/1000 is 3 in integer division.
Perhaps, you should rewrite your last line as if ((v/1000) >= 360) v = 0;. Otherwise, I would rewrite your loop like this
while(1){
v+=StepSize;
v/=1000;
DAC->DHR12R2 = sin(v);
if (v >= 360) v = 0;
}
I would also recommend that you declare your lookup table a const. So it would look like
const signed int sine_table[91] = {
Last recommendation is to choose another name for your sin function so as not to confuse with the sin library function. Even though in this case there shouldn't be a problem.
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