Discussion:
Why does int64_t * float get promoted to a float?
(too old to reply)
Jeff Koftinoff
2008-11-20 23:02:19 UTC
Permalink
It surprised me to see the following code, compiled with GNU GCC 4.0:

#include <stdint.h>
#include <iostream>

template <typename T>
void show( T a )
{
std::cout << "calls: " << __PRETTY_FUNCTION__ << std::endl;
}


#define compare(T1,T2) { T1 a = 100; T2 b = 666; std::cout << #T1 " *
" #T2 " "; show(a * b); }

int main()
{
compare(float,int64_t);
compare(int64_t,float);

compare(float,double);
compare(double,float);

compare(int8_t,int16_t);
compare(int16_t,int8_t);

compare(int32_t,uint16_t);
compare(uint16_t,int32_t);

compare(char,int32_t);
compare(int32_t,char);

compare(int64_t,int32_t);
compare(int32_t,int64_t);
}


It outputs the following:


float * int64_t calls: void show(T) [with T = float]
int64_t * float calls: void show(T) [with T = float]
float * double calls: void show(T) [with T = double]
double * float calls: void show(T) [with T = double]
int8_t * int16_t calls: void show(T) [with T = int]
int16_t * int8_t calls: void show(T) [with T = int]
int32_t * uint16_t calls: void show(T) [with T = int]
uint16_t * int32_t calls: void show(T) [with T = int]
char * int32_t calls: void show(T) [with T = int]
int32_t * char calls: void show(T) [with T = int]
int64_t * int32_t calls: void show(T) [with T = long long int]
int32_t * int64_t calls: void show(T) [with T = long long int]

It would have made more sense to me if a float * int64_t would be a
double, or even a int64_t, but not a float... What is the real rule?
I had just assumed that the type with the most precision would be
chosen.

--jeffk++
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
t***@gmail.com
2008-11-21 01:34:24 UTC
Permalink
Post by Jeff Koftinoff
It would have made more sense to me if a float * int64_t would be a
double, or even a int64_t, but not a float... What is the real rule?
I had just assumed that the type with the most precision would be
chosen.
left precedence, iirc.

operator<< is a little tenacious on mixing types, usually if there are
any chance of mixed types I put them on their own line for clarity.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Alberto Ganesh Barbati
2008-11-21 13:21:44 UTC
Permalink
Post by t***@gmail.com
Post by Jeff Koftinoff
It would have made more sense to me if a float * int64_t would be a
double, or even a int64_t, but not a float... What is the real rule?
I had just assumed that the type with the most precision would be
chosen.
left precedence, iirc.
Precedence is irrelevant. Both float * int64_t and int64_t * float
produce a float.
Post by t***@gmail.com
operator<< is a little tenacious on mixing types, usually if there are
any chance of mixed types I put them on their own line for clarity.
If you look carefully at the OP's code, you'll see that operator<< plays
no role at all.

Ganesh
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Alberto Ganesh Barbati
2008-11-21 01:34:09 UTC
Permalink
Post by Jeff Koftinoff
It would have made more sense to me if a float * int64_t would be a
double, or even a int64_t, but not a float... What is the real rule?
I had just assumed that the type with the most precision would be
chosen.
The rule is in clause 5, paragraph 9 and basically says that:

1) if one operand is long double, the other operand is converted to long
double

2) otherwise, if one operand is double, the other operand is converted
to double

3) otherwise, if one operand is float, the other operand is converted to
float

4) otherwise, both operands are integrals and there are few more rules,
which I won't list here for brevity, covering this case.

HTH,

Ganesh
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Marco Manfredini
2008-11-21 13:22:49 UTC
Permalink
Post by Jeff Koftinoff
It would have made more sense to me if a float * int64_t would be a
double, or even a int64_t, but not a float... What is the real rule?
I had just assumed that the type with the most precision would be
chosen.
Most doubles can't held a int64_t either.

The rules about promotion use only a weak definition of "more precise"
where floating point types are "better" than integral types. This is
because C++ does not mandate a specific implementation of those types.
A float might be a 32-bit long entity, as it is on most
implementations, but could also be equivalent to a long double with 128
bits on a different platform. If promotion would be performed along the
actual precision of the implementation's types, then any program would
be potentially ill-formed, because a common type might not exist or
prone to subtle bugs, because promotion yields an unexpected type.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Alberto Ganesh Barbati
2008-11-21 19:57:57 UTC
Permalink
Post by Marco Manfredini
Post by Jeff Koftinoff
It would have made more sense to me if a float * int64_t would be a
double, or even a int64_t, but not a float... What is the real rule?
I had just assumed that the type with the most precision would be
chosen.
Most doubles can't held a int64_t either.
Most doubles can't hold an int64_t *accurately*, but they can hold it
inaccurately. A typical 64-bit double can represent the range from about
-2^1023 to about 2^1023, so it is more than enough to include the whole
span of int64_t, which is from -2^31-1 to 2^31 (inclusive).
{ the poster likely meant "-2^63-1 to 2^63". -mod }
Even the
smaller 32-bit float has a sufficiently large span, as it can represent
numbers from about -2^127 to about 2^127.
Post by Marco Manfredini
The rules about promotion use only a weak definition of "more precise"
where floating point types are "better" than integral types.
Floating point types are better because, except for extremely exotic
architectures, a float is always able to represent even the largest
integer, although inaccurately. The error can be estimated and is
relatively small (a tiny fraction of the magnitude of the number).
Oppositely, if you tried to convert a large float to an integer type you
might introduce an error which can be of the same order of magnitude as
the number itself. From a computational point of view, an error so big
makes the conversion practically useless.

HTH,

Ganesh
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Greg Herlihy
2008-11-21 19:47:12 UTC
Permalink
Post by Jeff Koftinoff
#include <stdint.h>
#include <iostream>
template <typename T>
void show( T a )
{
std::cout << "calls: " << __PRETTY_FUNCTION__ << std::endl;
}
#define compare(T1,T2) { T1 a = 100; T2 b = 666; std::cout << #T1 " *
" #T2 " "; show(a * b); }
int main()
{
compare(float,int64_t);
...
Post by Jeff Koftinoff
}
float * int64_t calls: void show(T) [with T = float]
It would have made more sense to me if a float * int64_t would be a
double, or even a int64_t, but not a float... What is the real rule?
I had just assumed that the type with the most precision would be
chosen
I think that greater accuracy edges out greater precision in this
case. After all, when multiplying 0.5 by 1 (1uLL * 0.5f), should the
compiler choose 0.5, 0, or 1 as the "best" result (that is, the one
"least surprising" to the programmer)? In other words, although the
greater number of bits of the int64_t type may provide greater
precision, the greater representational flexibility of the float's
bits - is more likely to provide the more accurate result.

Greg
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Continue reading on narkive:
Loading...