6

This question has come up before, in particular here Should we generally use float literals for floats instead of the simpler double literals? but I was wondering if there were any better suggested solutions now we are in C++14 land and things like user defined literals and brace initialization exist.

The problem can be expressed as how to write a floating literal in a template function for floating point types

template <typename T> T foo( T x )
{
    static_assert( std::is_floating_point<T>::value, "" );

    T y = x * 101.0;

    return( y );
}

So the question boils down to how do we write "101.0", as it stands it's a double so what happens if I call foo using

float a = 42.0f;
float b = foo( a );

If I write "101.0f" in foo what happens if I call foo with a double, note 101.0 and 101.0f are not necessarily the same.

Also how do I guarantee that no extra code is produced to cast values?

Other suggestions include writing things like "static_cast( 101.0 )", "T( 101.0 )" or other hideous things!

Community
  • 1
  • 1
goneskiing
  • 1,659
  • 1
  • 13
  • 22
  • 3
    [conv.fpprom]: "A prvalue of type `float` can be converted to a prvalue of type double. The value is unchanged. This conversion is called *floating point promotion*." –  Dec 11 '14 at 22:59
  • 1
    How about doing `static_cast(100.0)`? – dom0 Dec 11 '14 at 23:05
  • Littering static_cast though the code gets pretty ugly when the function have reasonable numbers of literals. I was hoping for something a bit more terse! – goneskiing Dec 11 '14 at 23:17
  • Note that in longer expressions containing multiple variables and literals I'm not sure all float literals get converted to double at compile time if T is double ( I have a test case that shows this is not the case a least on VS 2013 ) – goneskiing Dec 11 '14 at 23:26

2 Answers2

2

A C++14-specific option would be to use a variable template

namespace detail
{
    template<typename F>
    F multiplier = F(101.0);
}

template <typename T> T foo( T x )
{
    static_assert( std::is_floating_point<T>::value, "" );

    T y = x * detail::multiplier<T>;

    static_assert(std::is_same<decltype(detail::multiplier<T>), T>{}, "");

    return( y );
}

Live demo

Praetorian
  • 106,671
  • 19
  • 240
  • 328
1
template <typename T> T foo( T x ){
  static_assert( std::is_floating_point<T>::value, "" );

  T y = x * foo_constants<T>::one_o_one;

  return( y );
}

Then simply define and specialise your constants in the foo_constants struct.

You probably don't even need the static assert anymore. You could even extend your function to other algebras (vector products, matrices, quaternions, etc.).

However, it doesn't meet your terseness requirement.

didierc
  • 14,572
  • 3
  • 32
  • 52
  • I did actually use this approach for a while. Having a template specialization for every literal value possible is a pain and seems to fall into the hideous work around bucket. At the moment my real code base has about 100 distinct literal values! I agree though that this technique is reasonable for constants like pi, where I still use it. – goneskiing Dec 11 '14 at 23:24
  • You don't necessarily need to put them in separate structures. All you need is for the template to be able to find them. Then *modulate* according to how you wish to bundle things. Pi is typically the kind of constant where promotion fails (any irrational value needs to be handled that way I guess, or using a templated contexpr). – didierc Dec 11 '14 at 23:29