0

Take this function template:

template <typename T>
T divby2(T a)
{
  return .5 * a;
}

Does there exist a way to specify (.5), a double constant, so that it won't be converted at run time into T when T != double (say, when T is float)?

Some ideas for alternative specifications of the .5 constant:

template <typename T>
T divby2(T a)
{
  return T(.5) * a;
}

template <typename T>
T divby2(T a)
{
  return (T(1) / T(2)) * a;
}
user1095108
  • 14,119
  • 9
  • 58
  • 116
  • 2
    by default .5 is a double constant – ZoomIn May 16 '13 at 09:55
  • 2
    No sure I follow. You want `.5` to be a `double` no matter what `T` is? If that is the case, do you also want the return type to be a `double` ? As written, what do you want returned when `T` is `int` and the input value is `7` ? Might wanna gel on that for a minute, then consider what is it you're *really trying to solve here*. – WhozCraig May 16 '13 at 09:55
  • @WhozCraig No, I want to prevent `.5` from converting to `float` at run time, compile time is fine. Assume there are no integer `T` instantiations – user1095108 May 16 '13 at 09:55
  • .5 is a double, unless you suffix it with l, L, f or F. l and L make it a long double, f and F make it a float. – Marius Bancila May 16 '13 at 09:59
  • How about `T(.5)`, will it always be a double? I don't think so, but will it get converted at run time? – user1095108 May 16 '13 at 10:00
  • @user1095108 how well do you think that will work (if at all) when `T` is `int`, or `long`, or *anything* besides a floating-point type ? – WhozCraig May 16 '13 at 10:01
  • @WhozCraig Those cases can be massaged out using SFINAE. – user1095108 May 16 '13 at 10:02
  • There are no decisions made about conversions at runtime. The decision to convert a value to another type or not is made at **compiletime**. `.5` will be a double, no matter what. `a` will be whatever template specialization you have, and the result type will be whatever `operator*(double, T)` gives. And as it stands, that result type will be converted to T, no matter what. And `T(0.5)` will be always a `T`. If any of those conversions are not defined, you get a **compile time** error, types have nothing to do with runtime (unless you have virtual functions and the like). – Arne Mertz May 16 '13 at 10:05
  • @ArneMertz In other words, `T(.5)` conversion will be made at compile time? Make this an answer and I'll accept. There is a difference between loading .5 into a register, then converting to a float and loading a float .5 into a register. – user1095108 May 16 '13 at 10:06
  • @user1095108 I hope that you don't have to micro-optimize single instructions in templates - if so, consider to actually overload the template with properly optimized functions for the types you have to optimize it for. – Arne Mertz May 16 '13 at 10:23

1 Answers1

6

There are no decisions made about conversions at runtime. The decision to convert a value to another type or not is made at compiletime.

  • .5 will be a double, no matter what.
  • a will be whatever template specialization you have, i.e. of type T
  • The result type of the multiplication (let's call it X) will be whatever operator*(double, T) gives. A double for all the builtin numbers, except for long double, where it gives a long double
  • Since you are returning a T, the X returned by the multiplication will be converted into a T
  • And T(0.5) will be always a T.

If any of those conversions or the operator* are not defined, you get a compile time error. Types have nothing to do with runtime (unless you have virtual functions and the like).

To your comment: T(.5) is an expression of type T. The conversion of the values will conceptually take place at runtime. However, the compiler is allowed to optimize that away, e.g. if T is int, the compiler would instantiate the T(.5) with int(.5) and could immediately optimize that to 0.

From your questions I assume that you might not be aware of the nature of templates. Templates are evaluated and instantiated at compiletime, unlike generic functions in some other languages. Template instantiation means, that the compiler generates independent functions for each type you use the template with. So e.g. if you use that function with T=double, T=int and T=long double in different places it would be as if you had written three functions:

double divby2(double a)
{
  return .5 * a;
}

int divby2(int a)
{
  return .5 * a;
}

long double divby2(long double a)
{
  return .5 * a;
}

In the first function, no conversions will take place at all, because everything is double. In the second function, the compiler knows that double multiplied by an int gives a double, but that double is converted back to int. You might get warnings about that. In the third function, the multiplication of a double and a long double gives a long double. Since the return type is a long double as well, everything is fine and you won't get warnings.

Arne Mertz
  • 24,171
  • 3
  • 51
  • 90
  • could optimize, but it is not required to? Does there exist a way to specify `.5` in a way that will force the compiler not to convert it at run time (in the sense of loading a float into a register, not first loading a double, then converting)? What happens if I specify .5, as T(1) / T(2)? Such ratios are evaluated at compile time AFAIK. – user1095108 May 16 '13 at 10:58
  • 1
    @user1095108 If you trust your compiler as far as to evaluate `T(1) / T(2)` at compile time, you can very well trust it to do `T(.5)` at compile time. By the way, by your logic `T(1) / T(2)` could be done at runtime either, since, well `T(1)` isn't much different from `T(.5)`, is it? A compiler isn't required to do `1+2` at compile time either, but well, at some point you have to make a difference between helping the compiler as much as possible with properly optimized code and assuming a downright idiotic compiler. – Christian Rau May 16 '13 at 11:05
  • I don'T get your problem. You want .5, which is a double, *not* to be converted at runtime, but you want it to be loaded as a float? That's a contradiction AFAICS. – Arne Mertz May 16 '13 at 11:08
  • @ArneMertz There exist rational numbers that cannot be converted accurately into any floating point number (e.g. 1/3). Say I specify the rational number as T(1) / T(3). What is going to happen? Will the number be represented as accurately as possible, or will it go through conversions? When will the ratio be evaluated? This is what was on my mind. – user1095108 May 16 '13 at 12:54
  • @ChristianRau I'm not trusting or assuming, I'm _asking_. – user1095108 May 16 '13 at 12:55
  • `T(1)/T(3)` means converting first, dividing then. If T is a floating point type, it's the same as saying `1./3.` or `1.f/3.f` or `1.L/3.L` – Arne Mertz May 16 '13 at 19:03
  • @user1095108 wether the compiler gerenates assembly for that code as `0.3333333333...` or as `1.0/3.0` or as `x = intToFloat(1), y = intToFloat(3), x/y` is not specified, its completely left to your compiler and optimizer. If you really want to know, you'll have to inspect the assembler. – Arne Mertz May 17 '13 at 07:55
  • 1
    @ArneMertz I'd be very happy if you could quote the relevant parts of the C++11 standard regarding this. – user1095108 May 17 '13 at 09:28
  • 1
    @user1095108 The standard does not really cover optimizations, the only thing that *allows* them is §1.4,8: *A conforming implementation may have extensions [...], provided they do not alter the behavior of any well-formed program.* For the standard, `T(1)/T(3)` means **do exactly this:** "take an int-literal 1, convert it to T, take an int-literal 3, convert it to T, divide the results", no shortcuts. Any optimization done by the compiler is an extension that is not further covered by the standard. – Arne Mertz May 17 '13 at 10:20
  • 1
    @user1095108 *"I'd be very happy if you could quote the relevant parts of the C++11 standard regarding this."* - Regarding what? There's no doubt that `T(1)` converts 1 into type `T`. And about everything else from this comment *Arne* explicitly said that the standard *doesn't specify it*, so no quotes. – Christian Rau May 17 '13 at 15:45
  • @ChristianRau It is not always advisable to believe what others write, that's why I've asked. For now, I'll accept, but look into this further. – user1095108 May 20 '13 at 12:56