C++11 auto. convert from float to long

6.3k views Asked by At

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
1

There are 1 answers

4
vsoftco On BEST ANSWER

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.