Last version
#include <stdio.h> // for input/output.
#include <math.h> // for mathmatic functions (log, pow, etc.)
// Values
#define MAXELM 1000 // Array size
#define MINVAL 0.1 // Minimum x value
#define MAXVAL 1.9 // Maximum x value
#define EXPVAR 1.4 // Exponent which makes the variation non linear. If set to 1, the variation will be linear.
#define ACRTPT (MINVAL + MAXVAL)/2 // Accurate point. This value is used to know where to compute with maximum accuracy. Can be set to a fixed value.
// Behavior
#define STRICT 0 // if TRUE: Return -1 instead of the floored (or closest if out of range) offset when (x) hasn't been calculated for this value.
#define PNTALL 0 // if TRUE: Print all the calculated values.
#define ASKFOR 1 // if TRUE: Ask for a x value then print the calculated ln value for it.
// Global vars
double results[MAXELM]; // Array to store computed values.
// Func: offset to var conversion
double getvar(int offset)
{
double x = (double)MINVAL + ((double)MAXVAL - (double)MINVAL) * (double)offset / (double)MAXELM;
if(x >= (double)ACRTPT)
x = pow(x - (double)ACRTPT, (double)EXPVAR) + (double)ACRTPT;
else
x = -pow((double)ACRTPT - x, (double)EXPVAR) + (double)ACRTPT;
// This ^ is the equation used when NONLIN = 1; to have a non linear repartition. Feel free to change it. The inverse equation is in `int getoffset(double)`.
return x;
}
// Func: var to offset conversion
int getoffset(double var)
{
double x = var;
if(x >= (double)ACRTPT)
x = pow(x - (double)ACRTPT, 1.0/(double)EXPVAR) + (double)ACRTPT;
else
x = -pow((double)ACRTPT - x, 1.0/(double)EXPVAR) + (double)ACRTPT;
// This ^ is the equation used when NONLIN = 1; to calculate offset with a non linear repartition. Feel free to change it (but it must be the inverse of the one in
// `double getvar(int)` for this to work.). These equations are tied, so you cannot modify one without modifying the other. They are here because
// `pow(negative, non-integer)` always returns `-nan` instead of the correct value. This 'trick' uses the fact that (-x)^(1/3) == -(x^(1/3)) to cicumvent the
// limitation.
int offset = (x - (double)MINVAL) * (double)MAXELM / ((double)MAXVAL - (double)MINVAL);
#if STRICT
if(getvar(offset) != var)
return -1;
return (offset < 0)?-1:(offset > (MAXELM - 1))?-1:offset;
#else
return (offset < 0)?0:(offset > (MAXELM - 1))?MAXELM - 1:offset;
#endif
}
// Func: main.
int main(int argc, char* argv[])
{
int offset;
for(offset = 0; offset < MAXELM; offset++)
results[offset] = log(getvar(offset));
#if PNTALL
for(offset = 0; offset < MAXELM; offset++)
{
printf("[log(%lf) = %lf] ", getvar(offset), results[offset]);
if(!((offset + 1) % 6))
printf("\n");
}
printf("\n");
#endif
#if ASKFOR
double x;
printf("log(x) for x = ");
scanf("%lf", &x);
if((offset = getoffset(x)) < 0)
printf("ERROR: Value for x = %lf hasn't been calculated\n", x);
else
printf("results[%d]: log(%lf) = %lf\n", offset, getvar(offset), results[offset]);
#endif
return 0;
}
Last versions's characteristics:
- Uses a fixed size array.
- Computes ONLY the stored values (won't calculate multiple values for one array cell).
- Uses functions to get offset from value and value from offset, so you don't have to store the values of which the
log
has been calculated.
Advantages over the last version:
- Does not use
cbrt
, uses pow
instead.
- Allows to specify the growth of calculus variable at compilation time. (So the values are more or less grouped around the accurate point (
ACRTPT
))
Third version
#include <stdio.h> // for input/output.
#include <math.h> // for mathmatic functions (log, pow, etc.)
// Values
#define MAXELM 1000 // Array size
#define MINVAL 0.1 // Minimum x value
#define MAXVAL 1.9 // Maximum x value
#define ACRTPT (MINVAL + MAXVAL)/2 // Accurate point. This value is used to know where to compute with maximum accuracy. Can be set to a fixed value.
// Behavior
#define NONLIN 1 // if TRUE: Calculate log values with a quadratic distribution instead of linear distribution.
#define STRICT 1 // if TRUE: Return -1 instead of the floored (or closest if out of range) offset when (x) hasn't been calculated for this value.
#define PNTALL 0 // if TRUE: Print all the calculated values.
#define ASKFOR 1 // if TRUE: Ask for a x value then print the calculated ln value for it.
// Global vars
double results[MAXELM]; // Array to store computed values.
// Func: offset to var conversion
double getvar(int offset)
{
double x = (double)MINVAL + ((double)MAXVAL - (double)MINVAL) * (double)offset / (double)MAXELM;
#if NONLIN
x = pow((x - ACRTPT), 3) + ACRTPT;
// This ^ is the equation used when NONLIN = 1; to have a non linear repartition. Feel free to change it. The inverse equation is in `int getoffset(double)`.
#endif
return x;
}
// Func: var to offset conversion
int getoffset(double var)
{
#if NONLIN
int offset = ((
cbrt(var - ACRTPT) + ACRTPT
// This ^ is the equation used when NONLIN = 1; to calculate offset with a non linear repartition. Feel free to change it (but it must be the inverse of the one in
// `double getvar(int)` for this to work.)
) - (double)MINVAL) * (double)MAXELM / ((double)MAXVAL - (double)MINVAL);
#else
int offset = (var - (double)MINVAL) * (double)MAXELM / ((double)MAXVAL - (double)MINVAL);
#endif
#if STRICT
if(getvar(offset) != var)
return -1;
return (offset < 0)?-1:(offset > (MAXELM - 1))?-1:offset;
#else
return (offset < 0)?0:(offset > (MAXELM - 1))?MAXELM - 1:offset;
#endif
}
// Func: main.
int main(int argc, char* argv[])
{
int offset;
for(offset = 0; offset < MAXELM; offset++)
results[offset] = log(getvar(offset));
#if PNTALL
for(offset = 0; offset < MAXELM; offset++)
{
printf("[log(%lf) = %lf] ", getvar(offset), results[offset]);
if(!((offset + 1) % 6))
printf("\n");
}
printf("\n");
#endif
#if ASKFOR
double x;
printf("log(x) for x = ");
scanf("%lf", &x);
if((offset = getoffset(x)) < 0)
printf("ERROR: Value for x = %lf hasn't been calculated\n", x);
else
printf("results[%d]: log(%lf) = %lf\n", offset, getvar(offset), results[offset]);
#endif
return 0;
}
This version is cleaner and easier to maintain than the previous ones. If you need anything else, please leave a comment.
You can configure its behavior using the macros at the top of the file.
Caracteristics:
- Uses a fixed size array.
- Computes ONLY the stored values (won't calculate multiple values for one array cell).
- Uses functions to get offset from value and value from offset, so you don't have to store the values of which the
log
has been calculated.
Second version
Well, here is my second solution. See below it for the original comment.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define MIN_INC 0.001 // This is the minimum increment. If its set to 0, when tmp will be equal to avg, it will never leave this state, since INC_MUL * (tmp - avg)^2 will be 0.
#define INC_MUL 0.2 // This is a number which influences the precision you will get. The smaller it is, the more precise you will be, and the greater will be your result array cardinality.
typedef struct {
double offset;
double value; // value = log(offset). Since the results are not linarly widespread, this is pretty important.
} logCalc;
// Here, we need to use a pointer on a logCalc pointer, since we want to actually SET the address of the logCalc pointer, not the address of one of its copies.
int MyLogCreate(logCalc** arr, double min, double max)
{
if((*arr) != NULL)
return 0;
unsigned int i = 0;
double tmp, avg = (max + min) / 2.0;
for( ; min < avg; min += (INC_MUL * ((avg - min) * (avg - min)) + MIN_INC))
{
(*arr) = (logCalc*)realloc((*arr), sizeof(logCalc) * (i + 1));
(*arr)[i].offset = min;
(*arr)[i++].value = log(min);
}
for(tmp = avg ; tmp < max; tmp += (INC_MUL * ((tmp - avg) * (tmp - avg)) + MIN_INC))
{
(*arr) = (logCalc*)realloc((*arr), sizeof(logCalc) * (i + 1));
(*arr)[i].offset = tmp;
(*arr)[i++].value = log(tmp);
}
return i;
}
int main(int argc, char** argv)
{
logCalc *myloglut = NULL;
unsigned int i,
t = MyLogCreate(&myloglut, .1, 1.9);
for(i = 0; i < (t-1); i++)
{
printf("[log(%lf) = %lf], ", myloglut[i].offset, myloglut[i].value);
if(!((i+1)%6)) // Change 6 to what's best for your terminal $COLUMNS
printf("\n");
}
printf("\n");
free(myloglut);
return 0;
}
Original comment
The linearity of your calculation comes from the fact that you're using a linear increment. On each iteration of your for loop, you increment exp
by (2.0 - 0.1) / MAXLOG
.
To get more precise values around 0, you will need:
- To define a larger range - a larger array - (to be able to store more values around 0)
- To use a non-linear increment. This increment will probably be dependent on
i
(or on exp
, depending on how you do it), so you know precisely the "offset" of the number you are trying to calculate (and the amount you need to increment exp
with). Of course, you will calculate more results around 0.
Here is my current implementation of it:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define CALCULATE_UNTIL 2.0
#define PRECISE_UNTIL 1.0
typedef struct {
double offset;
double value;
} logCalc;
logCalc *myloglut = NULL;
int MyLogCreate()
{
double exp = 0.1;
int i;
for (i = 0; exp <= CALCULATE_UNTIL; exp += (exp < PRECISE_UNTIL)?0.0001898:0.001898)
{
myloglut = realloc(myloglut, sizeof(logCalc) * (i + 1));
myloglut[i].offset = exp;
myloglut[i++].value = (i == 4780)?0:log(exp);
}
return i; // So you know how big the array is. Don't forget to free(myloglut); at the end of your code.
}
int main(int argc, char** argv)
{
int i,
t = MyLogCreate();
for(i = 0; i < t; i++)
{
printf("[log(%lf) = %lf], ", myloglut[i].offset, myloglut[i].value);
if(!(i%6)) // For formatting purposes.
printf("\n");
}
printf("\n");
free(myloglut);
return 0;
}
I've created a new type in order to store the value of exp too, which could be useful for knowing what value the result is the log of.
Update: I'm not sure about what you want to do. Do you want to be precise around log(x) = 0 or around x = 0? On the first case, I might have to re-write the code again for it to work as you want. Also, do you want the results to be more precise at it gets close to 0, or do you want the results to be more precise in a given range (as it is now)?