Search code examples
c++c++11castingauto

C++11 auto. convert from float to long


Is it possible to convert foo from float to long (and vice versa)?

auto foo = float(1234567891234.1234);
cout << "foo: " << foo << endl;

foo = long(1234567891234.1234);
cout << "foo: " << foo << endl;

The output is always:

foo: 1.23457e+12
foo: 1.23457e+12

Solution

  • Not in the way you wrote it. First,

    auto foo = float(1234567891234.1234);
    

    uses auto type deduction rules to infer the type of the RHS, and the result is float. Once this is done, the type of foo is float and it is set in stone (C++ is statically typed, unlike e.g. Python). When you next write

    foo = long(1234567891234.1234);
    

    the type of foo is still float and it is not magically changed to long.

    If you want to emulate a "change" of type you can at most perform a cast:

     cout << "foo (as long): " << static_cast<long>(foo) << endl;
    

    or use an additional variable

    long foo_long = foo; // again you may have a loss of precision 
    

    but be aware of possible precision loss due to floating point representation.

    If you have access to a C++17 compiler, you can use an std::variant<long, float>, which is a type-safe union, to switch between types. If not, you can just use a plain old union like

    #include <iostream>
    
    union Foo
    {
        float f;
        long l;
    };
    
    int main()
    {
        Foo foo;
        foo.f = float(1234567891234.1234); // we set up the float member
        std::cout << "foo: " << foo.f << std::endl;
    
        foo.l = long(1234567891234.1234); // we set up the long member
        std::cout << "foo: " << foo.l << std::endl;
    }
    

    Live on Coliru

    Or, you can use a type-erasure technique like

    #include <iostream>
    
    int main()
    {
        void *foo; // we will store the object via this pointer
    
        foo = new int{42};
        std::cout << *(int*)foo << '\n';
        operator delete(foo); // don't do delete foo, it is undefined behaviour
    
        foo = new float{42.42};
        std::cout << *(float*)foo << '\n';
        operator delete(foo); // don't do delete foo, it is undefined behaviour
    }
    

    Live on Coliru

    The modern version of the code above can be re-written with a std::shared_ptr like

    #include <iostream>
    #include <memory>
    
    int main()
    {
        std::shared_ptr<void> foo{new int{42}};
        std::cout << *(int*)foo.get() << '\n';
    
        foo.reset(new float{42.42});
        std::cout << *(float*)foo.get() << '\n';
    }
    

    Live on Coliru

    A std::unique_ptr<void> won't work as only std::shared_ptr implements type-erasure.

    Of course, if you don't really care about storage size etc, just use 2 separate variables.