0

TL;DR :

Is there a type (let's name it convert_type) that allows to do type casting without loosing information, even when dealing with the float type ?

Example of usage :

float f1 = 12.34;
long i1 = 32;

convert_type tmp1 = (convert_type) f;
convert_type tmp2 = (convert_type) i;

float f2 = (float) tmp1; // should be 12.34
long i2 = (long) tmp2; // should be 32

CONTEXT :

I want to create such a function :

convert_type ask(char* question, val_type input_type)

with the type val_typedefined by :

typedef enum {
    INT_T,
    STR_T,
    CHAR_T,
    FLOAT_T
} val_type;

The argument questionhas a string to print in order to ask the input.

The idea of this function is to ask in the stdin an input of type input_type, and ask again until the input is valid.

Example of usage of ask() :

int main(int argc, char const *argv[]) {
    int nb = (int) ask("How many tries to you want ?\n --> ", T_INT);
    char * message = (char*) ask("What is the victory message ?\n --> ", T_STR);
    float prob = (float) ask("What is the probability of winning ?\n --> ", T_FLOAT);
    printf("\n\n");
    printf("Number of tries : %d\n", nb);
    printf("Victory message : %s\n", message);
    printf("Probability : %f\n", prob);
    return 0;
}

I implemented it using the type unsigned long, and I have trouble with float type.

Example of execution :

How many tries to you want ?
 --> many
The input must be a valid integer.
How many tries to you want ?
 --> -2131 
What is the victory message ?
 --> We are the champions !!!
What is the probability of winning ?
 --> 3.4.3
The input must be a valid float value.
What is the probability of winning ?
 --> 2.54


Number of tries : -2131
Victory message : We are the champions !!!
Probability : 2.000000

It is noticeable that unsigned long doesn't work well with float type (the 2.54 became 2.00 because unsigned long contains an integer value I guess).

What type can I use instead ?

Naeio
  • 1,112
  • 7
  • 21
  • 1
    This is more a conversion than polymorphism. – Peter - Reinstate Monica Dec 07 '19 at 14:16
  • And if you are just interested in numbers you can always use the longest double your system provides; it will store [even large integers](https://stackoverflow.com/a/1848762/3150802) without a loss of precision, and can be converted to all number types (and the char flavors) with ordinary arithmetic conversions. Oh, and you can simply `scanf` integer text representations into doubles. (If you want to make sure the next character is not a dot or an e you'll need to do a bit more though.) – Peter - Reinstate Monica Dec 07 '19 at 14:18
  • @Peter-ReinstateMonica Nope, it doesn't work if I start using `long`and `unsigned long`. (Try converting `9223372036854775807`to `long double`, and back to `long`, you'll see that it's not the same value, because there is a loss of information when going from `long` to `long double`.) – Naeio Dec 07 '19 at 15:04
  • No, don't use long long ;-). – Peter - Reinstate Monica Dec 07 '19 at 16:55
  • What I mean is that I can't put an integer (that is not a power of 2) as large as `9223372036854775807` in a `long double`, without loss of information, so if I want my function to be able to return a `long` or `unsigned long`, I can't use `long double` as a return type – Naeio Dec 08 '19 at 18:21
  • True, that won't work. Old programmer wisdom: "Changing the manual is often easier than changing the program." – Peter - Reinstate Monica Dec 08 '19 at 18:43
  • Indeed xD But I'll stick on the `union`, to keep as much flexibility as possible. – Naeio Dec 09 '19 at 20:48

1 Answers1

1

If you are careful enough not to cause read/write type mismatch, you can do like this using union:

#include <stdio.h>

typedef union {
    int int_data;
    char* str_data;
    char char_data;
    float float_data;
} convert_type;

typedef enum {
    INT_T,
    STR_T,
    CHAR_T,
    FLOAT_T
} val_type;

convert_type ask(char* question, val_type input_type) {
    static char[] str = "We are the champions !!!";
    convert_type ret;
    fputs(question, stdout);
    /* this is examples of how to set values. reading is omitted. */
    switch (input_type) {
        case INT_T:
            ret.int_data = -2131;
            break;
        case STR_T:
            ret.str_data = str;
            break;
        case CHAR_T:
            ret.char_data = 'a';
            break;
        case FLOAT_T:
            ret.float_data = 2.54;
            break;
    }
    return ret;
}

int main(int argc, char const *argv[]) {
    int nb = ask("How many tries to you want ?\n --> ", INT_T).int_data;
    char * message = ask("What is the victory message ?\n --> ", STR_T).str_data;
    float prob = ask("What is the probability of winning ?\n --> ", FLOAT_T).float_data;
    printf("\n\n");
    printf("Number of tries : %d\n", nb);
    printf("Victory message : %s\n", message);
    printf("Probability : %f\n", prob);
    return 0;
}

Unlike struct, the members of union share one memory space. Therefore, when you write some value to a member of union, the other members of the union become invalid.

MikeCAT
  • 73,922
  • 11
  • 45
  • 70
  • In C, the other members of a union do not become invalid. Accessing a member of a union other than the last one stored is defined by the C standard to reinterpret the bytes as the new type (which generally involves implementation-defined behavior). – Eric Postpischil Dec 07 '19 at 13:39