mattkretz / wg21-papers

my papers to WG21 — the C++ committee
5 stars 7 forks source link

Add to_{fixed,native,compatible} cast functions #10

Closed mattkretz closed 7 years ago

mattkretz commented 7 years ago

To make explicit conversions easier there could be the following three functions:

datapar_to_fixed(datapar<T, Abi>) -> datapar<T, fixed_size<N>>
datapar_to_native(datapar<T, Abi>) -> datapar<T, native>
datapar_to_compatible(datapar<T, Abi>) -> datapar<T>
jensmaurer commented 7 years ago

Yes.

Off-topic:

Btw, I'm beginning to slightly warm up to the idea to have the "to_fixed" conversion as an implicit one, similar to the situation that the conversion of double to the more general complex<double> is implicit. It can produce non-portable code, though:

datapar<T,Abi> intpow(datapar<T,Abi> x, datapar<int, fixed<datapar<T,Abi>::size()>> n)

intpow(datapar<float>(2), datapar<int>(-2)) is fine on SSE2, but gives an error on AVX. And nobody says that float and int are the same size to start with.

mattkretz commented 7 years ago

You're making an important point. Now that I removed implicit conversions my math function signatures are problematic. E.g.

template <class Abi>
datapar<float, Abi> frexp(datapar<float, Abi> value,
                          datapar<int, abi_for_size_t<datapar<float, Abi>::size()>>* exp);

So this expects a native ABI for the int parameter for SSE and AVX2, but with AVX the second argument has to be a fixed_size ABI. This was easier to use before I restricted conversions between ABIs.

Both directions are conceivable. One user might work with:

using floatv = datapar<float>;
using intv = datapar<int, abi_for_size_t<floatv::size()>>;

The next with:

using floatv = datapar<float>;
using intv = datapar<int, fixed_size<floatv::size()>>;

Both should be supported scenarios. If "to_fixed" is implicit and math functions use fixed_size instead of abi_for_size in mixed signatures then we'd be much nicer to our users already. Even if there still is your counterargument.

It is impossible to make every unportable use ill-formed. We just need to optimize for the API that catches the majority of the unportable uses. In any case, the user who actually writes intpow(datapar<float>(2), datapar<int>(-2) should be punished (best by making the code ill-formed). The user who uses abi_for_size created a portable combination of datapar types, but is indistinguishable from the former.

Conclusion? Don't know yet. Keep it conservative until we know better? But I think I need to modify the math signatures to use fixed_size instead of abi_for_size.

jensmaurer commented 7 years ago

If we have an implicit conversion from datapar<T, Abi> to datapar<T, fixed_size<N>>, I think we support both of your user groups. Math functions should use fixed_size<N> for secondary parameters, yes. (That was one of the main results of the SG1 discussion asking for fixed_size<N> some years ago, I believe.) People using abi_for_size already get different types on SSE vs. AVX and thus are non-portable on the type.