How to make a variable float complex or double complex depending on user input? I'm trying to plot the Mandelbrot set and I have the same long code for float and double but in some cases float is not precise enough and double is too slow. I'm running it on a low-performance calculator, so float is less than 1 second and double takes at least 5 seconds. I'd like to let the user choose precision over speed or speed over precision. I would like to get something like this:
int user_input;
scanf("%d", &user_input);
switch (user_input) {
case 0:
float z;
float c;
break;
case 1:
double z;
double c;
break;
}
for(int i=0;i<1920;i++){
for(int j=0;j<1080;j++){
z=0.0+0.0*I;
c=(i+j*I)/Zoom
}}
Expected outcome: if the user types 0, then all the calculations are performed using floats, and if the user types 1, the calculations are performed using doubles.
But this throws error: conflicting types for ‘z’; have ‘double’, previous declaration of ‘z’ with type ‘float’.
Is something like this possible in C?
I tried making two different varables for float and double but changing them in all the code (I have way more code) would be long and tedious.
You can't change the type of a variable after it's been compiled, but you can use a function-like macro to make a different version of the same function for each type you might use without needing to duplicate code, much as you might use templates in other languages:
#include <stdio.h>
#define MANDELBROT(T) \
void mandelbrot_##T(T I, T Zoom) { \
T z, c; \
for(int i=0;i<1920;i++){ \
for(int j=0;j<1080;j++){ \
z=0.0+0.0*I; \
c=(i+j*I)/Zoom; \
} \
} \
}
MANDELBROT(double)
MANDELBROT(float)
int main(void) {
int user_input;
scanf("%d", &user_input);
double Zoom = 1.0, I = 1.0;
if (user_input) {
mandelbrot_double(I, Zoom);
}
else {
mandelbrot_float(I, Zoom);
}
}
If you've got a bunch of functions like this, and you don't want to declare absolutely everything as a macro (since putting backslashes after every line is annoying, and it makes it harder for debuggers to tell what lines things are defined on), you can instead do it like this:
templates.h:
/*
By wrapping all function names in the NAME macro, we get a different identifier
depending on whether we defined T as double or float before #including this file:
NAME("foo") will expand to "foo_double" if T is double.
Sometimes we have to do some minor wizardry to get a macro to fully expand,
which is why we have NAME2 and NAME3. Passing a macro to another
macro sets a dirty-bit that causes it to be expanded properly.
*/
#define NAME3(S,U) S ## _ ## U
#define NAME2(S,U) NAME3(S,U)
#define NAME(S) NAME2(S,T)
void NAME(mandelbrot)(T I, T Zoom) {
T z, c;
for(int i=0;i<1920;i++){
for(int j=0;j<1080;j++){
z=0.0+0.0*I;
c=(i+j*I)/Zoom;
}
}
}
#undef T
#undef NAME
#undef NAME2
#undef NAME3
main.c:
#define T double
#include "templates.h"
#define T float
#include "templates.h"
int main(void) {
mandelbrot_double(1,1);
mandelbrot_float(1,1);
}
A side note: as chux pointed out in the comments, I
is a macro from complex.h, so you should name that something else (unless that macro is what you were intending to use).
You can't. The compiler needs to know the type of the variable in order to generate the appropriate code.
In your scenario, I would create a program that could be compiled into two different executables: one that uses float
, and one that uses double
.
#ifdef USEDOUBLE
typedef double Number;
#else
typedef float Number;
#endif
Number z;
gcc plot.c -o plot_float
gcc -DUSEDOUBLE plot.c -o plot_double
Another possibility is to create two versions of everything. You don't want to actually write two of everything, so you'd use the earlier approach to create two libraries, then load the appropriate one at run-time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With