Topic: Possible Defect Report: sqrt(1) ambiguity.


Author: Ralf Sinoradzki <r_sinora@informatik.uni-kl.de>
Date: Wed, 13 Mar 2002 18:04:46 CST
Raw View
Sorry, I was a bit confused,

I wrote:
> // max(a,b)-template, that looks up it's return type
>
> template <class T1, class T2>
> Res<T1,T2>::Type max(T1 a, T2 b){
>     return (a>b) ? a : b;
> }

A simple specialized Res-template for the parameter pairs
would have done the same and is easier to understand
although I'd have to implement every pair of arguments twice ...

regards, Ralf

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Rudy Velthuis <rvelthuis@gmx.de>
Date: Sat, 16 Mar 2002 01:57:16 GMT
Raw View
In article <k5jj8uggf5nk0bpnhbtbpbn2ecpp71peol@4ax.com>, Robert W Hand
says...

> will assign to d the value of the product of two doubles.  From the
> programmer's point of view, there is no loss of precision.  If
> different precision is required, the programmer could use sqrtl or
> sqrtf as he or she needed.

But, as someone said, these functions might be used in templates, in
C++. Having functions of the same name is quite handy, in that case.
--
Rudy Velthuis (TeamB)

"Object-oriented programming is an exceptionally bad idea which
 could only have originated in California." -- Edsger Dijkstra

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Rudy Velthuis <rvelthuis@gmx.de>
Date: Sat, 16 Mar 2002 16:12:34 GMT
Raw View
In article <fl6645jvxy.fsf@jambon.cmla.ens-cachan.fr>, Gabriel Dos Reis
says...

> int -> double is the way C99 handles that issue.  That also is the
> historical way that issue has been handled no red herring.

I guess there was a reason to specify three overloads in C++.

> If the user no more than the usual promotion, then he is reponsible to
> say so, explicitly.

But AFAIK, there *is* no usual promotion. I gave arguments against it.
Simply saying "but that is how we have always done it in C" does not
really convince me as an argument.

> | E.g. on Pentiums, float is usually
> | the same size as an int (4 bytes), so its mantissa is smaller than an
> | int, which can result in a loss of least significant bits.
>
> But we're taling of *double*, right?

Yes, but the float was just a real life example for Win32, and only to
demonstrate how choosing one type over the other could lead to precision
loss. The same could be true for a double on other platforms. And of
course for long double as well, on some platforms, but then there is no
alternative anymore. As long as an alternative exists, one should not be
forced to a default that may not fit.

> And presumably there is no guarantee that a long double would have
> more precision.  That is a red herring.

Exactly that is why I don't see why there should be a preferred, or
default, or usual promotion from int to double at all.
--
Rudy Velthuis (TeamB)

"Object-oriented programming is an exceptionally bad idea which
 could only have originated in California." -- Edsger Dijkstra

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Graeme Prentice <gp1@paradise.net.nz>
Date: Mon, 11 Mar 2002 20:03:36 GMT
Raw View
On Sun, 10 Mar 2002 23:48:51 GMT, Gabriel Dos Reis
<dosreis@cmla.ens-cachan.fr> wrote:

>Howard Hinnant <hinnant@antispam.metrowerks.com> writes:
>
>[...]
>
>| We actually shipped the template solution once and found this out the
>| hard way.  This demo isn't hypothetical.  The need to restrict the
>| template to integral (or arithmetic) types only is quite real.
>
>I'm wondering why the template solution was retained, where
>overloading would have done the job...

Perhaps because you'd need at least 4 overloaded functions with
parameters int, unsigned int, long, unsigned long  (I think) and
(maybe one day long long)  - makes a bigger header file and lib. I
guess. If you didn't have all four you'd get ambiguities I think.

I'm wondering how a single template function could call the
appropriate one of three math functions for C++ code   - float
argument calls float version, any integer type or double calls double,
long double calls long double ??   (without using the compiler magic
[i.e. the compiler supplied Typeof operator?] )

If you multiply the parameter by 1.0 you convert a float to double
which would call the wrong function via overloading.  If you multiply
the parameter by 1.0f then integer types convert to float which call
the wrong function.  Using sizeof wouldn't work I don't think.

Maybe there's a common do nothing inline type conversion function
overloaded for all types that returns the correct type.
inline double gettype(int v) { return v; }  // and others
inline float gettype(float v) { return v; }
inline double gettype(double v) { return v;}  // etc
Maybe a template function with specialisations.

Then the template math function calls gettype()  - would this work?
e.g. sqrt( gettype(v) );

Regarding the template function breaking existing code  - this would
only happen if you include tgmath.h so it only breaks if you "ask it
to" and I suspect wouldn't be much of a problem  -  I could be wrong.

What would be wrong with changing the overload resolution rule to
favour double (or long double) for an integral argument if there's an
ambiguity between two or more float types   - it wouldn't break
existing code because existing code doesn't compile in that situation.

Shouldn't any mapping functionality allow for different relative sizes
of int, long, long long compared to the floating types and avoid
losing precision whenever possible  i.e. instead of always mapping an
integral type to double as C99 does, instead choose long double if
double can lose precision.

Graeme.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "P.J. Plauger" <pjp@dinkumware.com>
Date: Mon, 11 Mar 2002 21:35:36 GMT
Raw View
"Graeme Prentice" <gp1@paradise.net.nz> wrote in message news:874p8ugebjqvgj9vp74hifn403bbbbr72d@4ax.com...

> I'm wondering how a single template function could call the
> appropriate one of three math functions for C++ code   - float
> argument calls float version, any integer type or double calls double,
> long double calls long double ??   (without using the compiler magic
> [i.e. the compiler supplied Typeof operator?] )

No compiler magic needed. Just the usual Stupid Template Tricks.

> Regarding the template function breaking existing code  - this would
> only happen if you include tgmath.h so it only breaks if you "ask it
> to" and I suspect wouldn't be much of a problem  -  I could be wrong.

I also believe it's not much of a problem, being ghettoized within
<tgmath.h>.

> What would be wrong with changing the overload resolution rule to
> favour double (or long double) for an integral argument if there's an
> ambiguity between two or more float types   - it wouldn't break
> existing code because existing code doesn't compile in that situation.

Tinkering with the basic conversion rules at this stage would be
EXTREMELY dangerous.

> Shouldn't any mapping functionality allow for different relative sizes
> of int, long, long long compared to the floating types and avoid
> losing precision whenever possible  i.e. instead of always mapping an
> integral type to double as C99 does, instead choose long double if
> double can lose precision.

An interesting notion. Our goal, however, was to match C99 rules, which
in turn sort of match Fortran rules.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "Fernando Cacciola" <fcacciola@gosierra.com>
Date: Mon, 11 Mar 2002 22:21:06 GMT
Raw View
"Graeme Prentice" <gp1@paradise.net.nz> wrote in message
news:874p8ugebjqvgj9vp74hifn403bbbbr72d@4ax.com...
> On Sun, 10 Mar 2002 23:48:51 GMT, Gabriel Dos Reis
> <dosreis@cmla.ens-cachan.fr> wrote:
>
> >Howard Hinnant <hinnant@antispam.metrowerks.com> writes:
> >
> >[...]
> >
> >| We actually shipped the template solution once and found this out the
> >| hard way.  This demo isn't hypothetical.  The need to restrict the
> >| template to integral (or arithmetic) types only is quite real.
> >
> >I'm wondering why the template solution was retained, where
> >overloading would have done the job...
>
> Perhaps because you'd need at least 4 overloaded functions with
> parameters int, unsigned int, long, unsigned long  (I think) and
> (maybe one day long long)  - makes a bigger header file and lib. I
> guess. If you didn't have all four you'd get ambiguities I think.
>
> I'm wondering how a single template function could call the
> appropriate one of three math functions for C++ code   - float
> argument calls float version, any integer type or double calls double,
> long double calls long double ??   (without using the compiler magic
> [i.e. the compiler supplied Typeof operator?] )
>
>
Well, clearly, this can always be done.
For example, the following illustrates a possible implementation (which I
post here along with a test to show that it does work)


// possible portion of a vendor specific <cmath>
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

// Assume we have the low-level float/double/long double versions.
namespace vendor {

float sqrtf ( float ) { return float(0) ; }
double sqrtd ( double ) { return double(0) ; }
long double sqrtld ( long double ) { return (long double)(0) ; }

// The behaviour of the call:
//
//   sqrt(x), where x is of an arbitrary type T
//
// is determined by vendor::sqrt_traits<T> as follows
//
//  argument_type:  the argument and result type
//                  of the low level implementation function being called.
//
//  function_type: the low level implementation function being called.
//
//  result_type: the result type of sqrt(x)
//
//
template<class>
struct sqrt_traits
{
  typedef double argument_type ;

  typedef argument_type (*function_type) ( argument_type ) ;

  static function_type call() { return &sqrtd; }

  typedef double result_type ;
} ;

} // namespace vendor

namespace stdx { // this namespace would be 'std' in real code.


// The unique sqrt() template version, which actually delegates everything
to
// sqrt_traits<T>.
//
template<class T>
typename vendor::sqrt_traits<T>::result_type sqrt ( T x )
{
  typedef vendor::sqrt_traits<T> traits ;

  typedef typename traits::result_type   result_type ;
  typedef typename traits::argument_type argument_type ;

  argument_type arg = static_cast<argument_type>(x) ;
  argument_type result = (*traits::call())(arg);

  return static_cast<result_type>(result) ;
}

} // namespace stdx  // which would actually be std

namespace vendor {

// This defines the std conforming behaviour for 'float' arguments.
template<>
struct sqrt_traits<float>
{
  typedef float argument_type ;

  typedef argument_type (*function_type) ( argument_type ) ;

  static function_type call() { return &sqrtf; }

  typedef float result_type ;
} ;

// This defines the std conforming behaviour for 'long double' arguments.
template<>
struct sqrt_traits<long double>
{
  typedef long double argument_type ;

  typedef argument_type (*function_type) ( argument_type ) ;

  static function_type call() { return &sqrtld; }

  typedef long double result_type ;
} ;

// The std conforming behaviour for 'double' arguments is gained by usong
// using the default traits.

// Additionally, other argument types, such as integral types, behave as if
the
// argument type were double, returning double.

} // namespace vendor





// numeric_package.hpp  (user defined)
// ~~~~~~~~~~~~~~~~~~~

namespace MyNumericPackage
{
  struct Real {} ;
  Real sqrt ( Real ) { return Real() ; }
}

// The user can coordinate with vendor's sqrt like this:

namespace vendor {

template<>
struct sqrt_traits< MyNumericPackage::Real >
{
  typedef MyNumericPackage::Real argument_type ;

  typedef argument_type (*function_type) ( argument_type ) ;

  static function_type call() { return &MyNumericPackage::sqrt ; }

  typedef MyNumericPackage::Real result_type ;
} ;

} // namespace vendor


// test.cpp
// ~~~~~~~~

#include <iostream>
#include <string>
#include <typeinfo>

template<class T> std::string show_type ( T x ) { return typeid(x).name()
; }

#define TEST(call) std::cout << #call << " used overload for " <<
how_type( stdx:: call ) << "\n"

int main()
{
  // This test should output..
  //
  //  sqrt('a') used overload for double
  //  sqrt(1)  used overload for double
  //  sqrt(1.0f)  used overload for float
  //  sqrt(1.0)  used overload for double
  //  sqrt(1.0l)  used overload for long double
  //  sqrt(MyNumericPackage::Real() ) )  used overload for
MyNumericPackage::Real

  TEST ( sqrt('a')  ) ;
  TEST ( sqrt(1)    ) ;
  TEST ( sqrt(1.0f) ) ;
  TEST ( sqrt(1.0)  ) ;
  TEST ( sqrt(1.0l) ) ;
  TEST ( sqrt(MyNumericPackage::Real() ) ) ;

  return 0 ;
}







---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Gabriel Dos Reis <dosreis@cmla.ens-cachan.fr>
Date: Mon, 11 Mar 2002 23:12:20 GMT
Raw View
Graeme Prentice <gp1@paradise.net.nz> writes:

| On Sun, 10 Mar 2002 23:48:51 GMT, Gabriel Dos Reis
| <dosreis@cmla.ens-cachan.fr> wrote:
|
| >Howard Hinnant <hinnant@antispam.metrowerks.com> writes:
| >
| >[...]
| >
| >| We actually shipped the template solution once and found this out the
| >| hard way.  This demo isn't hypothetical.  The need to restrict the
| >| template to integral (or arithmetic) types only is quite real.
| >
| >I'm wondering why the template solution was retained, where
| >overloading would have done the job...
|
| Perhaps because you'd need at least 4 overloaded functions with
| parameters int, unsigned int, long, unsigned long  (I think) and
| (maybe one day long long)  - makes a bigger header file and lib.

That is the job of implementors.  However, putting in a template
function that globbes anything makes the library unusable.

[...]

| Regarding the template function breaking existing code  - this would
| only happen if you include tgmath.h so it only breaks if you "ask it
| to" and I suspect wouldn't be much of a problem  -  I could be wrong.

That is one reason why inclusion of <tgmath.h> either in user codes
or in the next version of C++ should be given enought thought

--
Gabriel Dos Reis, dosreis@cmla.ens-cachan.fr

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "P.J. Plauger" <pjp@dinkumware.com>
Date: Tue, 12 Mar 2002 00:18:29 GMT
Raw View
"Fernando Cacciola" <fcacciola@gosierra.com> wrote in message news:a6j8cm$eppfd$1@ID-44132.news.dfncis.de...

> > >I'm wondering why the template solution was retained, where
> > >overloading would have done the job...
> >
> > Perhaps because you'd need at least 4 overloaded functions with
> > parameters int, unsigned int, long, unsigned long  (I think) and
> > (maybe one day long long)  - makes a bigger header file and lib. I
> > guess. If you didn't have all four you'd get ambiguities I think.
> >
> > I'm wondering how a single template function could call the
> > appropriate one of three math functions for C++ code   - float
> > argument calls float version, any integer type or double calls double,
> > long double calls long double ??   (without using the compiler magic
> > [i.e. the compiler supplied Typeof operator?] )
> >
> >
> Well, clearly, this can always be done.
> For example, the following illustrates a possible implementation (which I
> post here along with a test to show that it does work)

Reasonable start, but sqrt in <tgmath.h> also has to handle the three
complex types. Where it REALLY gets interesting is with a two-argument
function, such as pow, where pow(long double, float _Complex) has to
promote both arguments to long double _Complex. There are about 14
different formats for the functions declared in <complex.h> and
<math.h>.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Carlos Moreno <moreno_at_mochima_dot_com@mo.com>
Date: Tue, 12 Mar 2002 19:08:54 GMT
Raw View
Andrew Koenig wrote:

>
> In particular, in a context such as
>
>         long double x = sqrt(2);
>
> I am concerned that sqrt(2) might be evaluated as double, which would
> cause a quiet loss of precision.


Well, if a programmer is so "distracted" (that's how we call the
irresponsible programmers nowadays  ;-)) to code sqrt(2) when it
was critical that the result be returned as a long double and NOT
as a double, what tells you that they wouldn't code sqrt(2.0)
instead??

If losing the precision from long double to double is that critical,
do you think the coder will forget to do sqrt (long double(2)) ???

After this discussion, I don't think I would ever forget!  :-)

Carlos
--

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Ralf Sinoradzki <r_sinora@informatik.uni-kl.de>
Date: Wed, 13 Mar 2002 16:53:59 CST
Raw View
Sorry,
   a strange suggestion is following ;(

I've read this threads and have perhaps an idea for a workaround
with templates, until it is fixed in the standard.
What I tried to do is implementing simple rules to calculate
the return type.
As an example I choose a very simple template function 'max'
that determines the best return type from the two arguments
The rule is: 'int' < 'float' < 'double' < 'long double'.
I don't know, how long a full set of rules will be, so perhaps
my idea is bad. ( By the way, it works with GCC, I hope its
really standard C++ ... )
To simplify my rule, I chose IDs that are a power of two.

#include <iostream>

// mapping types to IDs ...

template <class T>
struct TYPE {};
template <>
struct TYPE<int> { enum{ ID = 1 }; };
template <>
struct TYPE<float> { enum{ ID = 2 }; };
template <>
struct TYPE<double> { enum{ ID = 4 }; };
template <>
struct TYPE<long double> { enum{ ID = 8 }; };

// mapping IDs to types ...

template <int n>
struct ID { typedef typename ID<n-1>::Type Type; };
template <>
struct ID<0> {}; // just to make sure it stops, if there is a bug
template<>
struct ID<1> { typedef int Type; };
template<>
struct ID<2> { typedef float Type; };
template<>
struct ID<4> { typedef double Type; };
template<>
struct ID<8> { typedef long double Type; };

// the rule that chooses the right type from the 2 arguments ...

template <class T1, class T2>
struct Res{
     enum { id = TYPE<T1>::ID + TYPE<T2>::ID - 1 } ;
     typedef typename ID<id>::Type Type;
};

// max(a,b)-template, that looks up it's return type

template <class T1, class T2>
Res<T1,T2>::Type max(T1 a, T2 b){
     return (a>b) ? a : b;
}

int main(){
     std::cout << max(1.0,2) << std::endl;
}

regards, Ralf

P.S: Perhaps somebody has an idea how to use it, otherwise:
      sorry for this weird idea :)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Carlos Moreno <moreno_at_mochima_dot_com@mo.com>
Date: Fri, 8 Mar 2002 19:09:53 GMT
Raw View
Fernando Cacciola wrote:

>
> Is there anything that can be done?
> Carlos suggested that (at least for std math C functions),  promotion and
> overload resolution should work such that 'double' is preferred as the
> promoted type so that the overload picks the 'right' function. (right in
> terms of common practice)


Actually, what I suggested is that the problem should be solved -- not
that overloading resolution rules should make an exception for standard
library math functions.

In particular, my concern after seeing the phrasing in the standard
is: "was it really the intention of the standard to make sqrt(1)
ambiguous?"  I can't see a sane reason why anyone would *want* sqrt(1)
to be ambiguous.

So, if the standard phrasing was something like this:

"In addition to the existing C fucntion, there will be the overloaded
versions for float and long double, but the use of sqrt with any other
numeric type implicitly convertible to double shall work as it works
in C"  (well, or something along those lines)

Then it would be clear that compiler vendors (more specifically, the
standard library writers) would be forced to write <cmath> in a way
that solevs the problem (there is no need to change the rules of
overloading resolution to achieve the above).  One way would be
providing overloaded versions for all of the numeric built-in
types, or another one using a template:

double sqrt(double);
long double sqrt(long double);
float sqrt(float);

template <typename T>
double sqrt (T x)
{
     return sqrt (static_cast<double> (x));
}


The above would fail for any type that is not implicitly convertible
to double -- unless there is another overloaded version of sqrt for
it, such as sqrt (std::complex<>)


But regardless...  My concern is:  are we talking about a defect in
the standard?  An oversight or an ambiguous phrasing?  Or was it
really the intention of the standard that a call to sqrt(int) *should*
be ambiguous?


Carlos
--

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Andrew Koenig <ark@research.att.com>
Date: Fri, 8 Mar 2002 20:19:26 GMT
Raw View
Carlos> Fernando Cacciola wrote:

Carlos> In particular, my concern after seeing the phrasing in the
Carlos> standard is: "was it really the intention of the standard to
Carlos> make sqrt(1) ambiguous?"  I can't see a sane reason why anyone
Carlos> would *want* sqrt(1) to be ambiguous.

Because otherwise someone needs to decide what type the result should
have, and any possible decision will sometimes be wrong.

In particular, in a context such as

        long double x = sqrt(2);

I am concerned that sqrt(2) might be evaluated as double, which would
cause a quiet loss of precision.  Such errors can be hard to trace.

--
Andrew Koenig, ark@research.att.com, http://www.research.att.com/info/ark

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Howard Hinnant <hinnant@antispam.metrowerks.com>
Date: Fri, 8 Mar 2002 21:00:13 GMT
Raw View
In article <3C88CA29.2060601@mo.com>, Carlos Moreno
<moreno_at_mochima_dot_com@mo.com> wrote:

| Then it would be clear that compiler vendors (more specifically, the
| standard library writers) would be forced to write <cmath> in a way
| that solevs the problem (there is no need to change the rules of
| overloading resolution to achieve the above).  One way would be
| providing overloaded versions for all of the numeric built-in
| types,

This is the way that Metrowerks solves this problem now.  And like I
said in a previous posting, it is not a pretty solution.  The header
that implements this is over 3000 lines of very redundant code.

| or another one using a template:
|
| double sqrt(double);
| long double sqrt(long double);
| float sqrt(float);
|
| template <typename T>
| double sqrt (T x)
| {
|      return sqrt (static_cast<double> (x));
| }

This solution can break user code.  Consider:

#include <cmath>

using namespace std;

struct SmallNum {};

struct BigNum
{
   BigNum() {}
   BigNum(const SmallNum&);
};

BigNum sqrt(const BigNum&);

int main()
{
   SmallNum y;
   BigNum x = sqrt(y);
}

Today this code works.  y implicitly converts to BigNum and then the
BigNum overload of sqrt is called.

But if you introduce the templated solution, sqrt(y) will bind to:

double sqrt<SmallNum>(SmallNum);

instead of to:

BigNum sqrt(const BigNum&);

Thus the motivation for the /restricted/ template that I introduced in
my previous posting.

--
Howard Hinnant
Metrowerks

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "Fernando Cacciola" <fcacciola@gosierra.com>
Date: Fri, 8 Mar 2002 21:01:33 GMT
Raw View
"Andrew Koenig" <ark@research.att.com> wrote in message
news:yu99zo1ivoii.fsf@europa.research.att.com...
> Carlos> Fernando Cacciola wrote:
>
> Carlos> In particular, my concern after seeing the phrasing in the
> Carlos> standard is: "was it really the intention of the standard to
> Carlos> make sqrt(1) ambiguous?"  I can't see a sane reason why anyone
> Carlos> would *want* sqrt(1) to be ambiguous.
>
> Because otherwise someone needs to decide what type the result should
> have, and any possible decision will sometimes be wrong.
>
>
This is true in the general case. For instance, a template based version
MUST be of the form

template<class T> T sqrt ( T ) ;

and not

template<class T> double sqrt ( T ) ;

which if used with T->int would truncate the result..., too bad.

> In particular, in a context such as
>
>         long double x = sqrt(2);
>
> I am concerned that sqrt(2) might be evaluated as double, which would
> cause a quiet loss of precision.  Such errors can be hard to trace.
>
OTOH, I still think that the issue could be resolved not for the general
case but for the particular case, at least, by defining an additional
required overload of the form:

double sqrt ( int ) ;

The point at stake here is not that the current specification is wrong, but
rather that is is TOO contrary to the common practice, even if that common
practice can be considered wrong.

I don't think that under this view, the example you showed can be a real
problem, because that call to sqrt(2) actually returned a double result
before (and for decades).

Furthermore, I wouldn't be really surprised to see that

  long double x = sqrt(2);

looses precision even if it never behaved this way before (which is not the
case), because this call

  long double x = sqrt(2.0);

looses just exactly the same precision, and it is and was perfectly legal.


--
Fernando Cacciola
Sierra s.r.l.
fcacciola@gosierra.com
www.gosierra.com






---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "P.J. Plauger" <pjp@dinkumware.com>
Date: Sat, 9 Mar 2002 05:45:12 GMT
Raw View
"Howard Hinnant" <hinnant@antispam.metrowerks.com> wrote in message
news:080320021509369559%hinnant@antispam.metrowerks.com...

> In article <3C88CA29.2060601@mo.com>, Carlos Moreno
> <moreno_at_mochima_dot_com@mo.com> wrote:
>
> | Then it would be clear that compiler vendors (more specifically, the
> | standard library writers) would be forced to write <cmath> in a way
> | that solevs the problem (there is no need to change the rules of
> | overloading resolution to achieve the above).  One way would be
> | providing overloaded versions for all of the numeric built-in
> | types,
>
> This is the way that Metrowerks solves this problem now.  And like I
> said in a previous posting, it is not a pretty solution.  The header
> that implements this is over 3000 lines of very redundant code.

We've come up with a different, and I think more elegant solution.
The C99 header <tgmath.h> magically adds generic math functions to
Standard C. Aside from the obvious cos((long double)3) being the same
as cosl(3), the header assures that cos(2) is the same as cos(double)2).
To do this right in C truly takes compiler magic, but in C++ we simply
provide a template function for each math function that Does the Right
Thing. It calls the appropriate one of the three precisions of a math
function by the required Fortran-ish rules of tgmath.h. The total C++
code required is just over 300 lines, 1/3 of which is needed for the
math.h functions fpclassify, etc. anyway.

So the simple rule is, include <tgmath.h> and cos(2) unambiguously
calls the double version. Don't include it and you get an ambiguity.
Equally important, including <tgmath.h> gives the same overload
resolution when compiling as either C99 or C++.

This is part of the C99/C++ reconciliation proposal we're preparing
for the next meeting.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Gabriel Dos Reis <dosreis@cmla.ens-cachan.fr>
Date: Sat, 9 Mar 2002 10:21:53 GMT
Raw View
Andrew Koenig <ark@research.att.com> writes:

| Carlos> Fernando Cacciola wrote:
|
| Carlos> In particular, my concern after seeing the phrasing in the
| Carlos> standard is: "was it really the intention of the standard to
| Carlos> make sqrt(1) ambiguous?"  I can't see a sane reason why anyone
| Carlos> would *want* sqrt(1) to be ambiguous.
|
| Because otherwise someone needs to decide what type the result should
| have, and any possible decision will sometimes be wrong.
|
| In particular, in a context such as
|
|         long double x = sqrt(2);
|
| I am concerned that sqrt(2) might be evaluated as double, which would
| cause a quiet loss of precision.  Such errors can be hard to trace.

Agreed in principle.  However, we have existing practice which have it
that such expressions ahev always been evaluated as a double -- I'm
not sure we have enought reasons to break such existing codes.

On the other hand, I think that given the pre-standard semantics of
sqrt(), I bet that if someone wanted the above to be evaluated as a
long double, it wouldn't have written it that way.

--
Gabriel Dos Reis, dosreis@cmla.ens-cachan.fr

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Rudy Velthuis <rvelthuis@gmx.de>
Date: Sat, 9 Mar 2002 16:37:36 GMT
Raw View
In article <fl8z93kq7v.fsf@jambon.cmla.ens-cachan.fr>, Gabriel Dos Reis
says...

> | Is there anything that can be done?
> | Carlos suggested that (at least for std math C functions),  promotion and
> | overload resolution should work such that 'double' is preferred as the
> | promoted type so that the overload picks the 'right' function. (right in
> | terms of common practice)
>
> That is a sensible proposal.

But please remember that *preferring* int -> double could result in a
loss of precision on some FPUs, which could have a double type with a
mantissa that is smaller than an int. E.g. on Pentiums, float is usually
the same size as an int (4 bytes), so its mantissa is smaller than an
int, which can result in a loss of least significant bits. The same
could be true for other CPU/FPU combinations and int -> double.
--
Rudy Velthuis

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Robert W Hand <robertwhand@netscape.net>
Date: Sat, 9 Mar 2002 19:15:35 GMT
Raw View
On Fri,  8 Mar 2002 20:19:26 GMT, Andrew Koenig <ark@research.att.com>
wrote:


>
>Because otherwise someone needs to decide what type the result should
>have, and any possible decision will sometimes be wrong.
>
>In particular, in a context such as
>
>        long double x = sqrt(2);
>
>I am concerned that sqrt(2) might be evaluated as double, which would
>cause a quiet loss of precision.  Such errors can be hard to trace.

IMHO, you have identified the real problem with overloading sqrt and
similar simple mathematical functions (except for abs).  The type of
the argument is not of prime importance.  Presumably the user will use
an argument type that will reflect accurately the precision of the
argument's value.  Rather the user is interested in the precision of
the value returned from the function.  Overload resolution is a clumsy
way to handle it.

I believe that this is a case where the name of the function should
identify the type of the returned value.  C (C99) has solved it by
defining new functions (since C90) sqrtf and sqrtl.  There is also a
generic header <tgmath.h> to make the code look more generic.  So code
such as:

double d = 3.0*sqrt(2);

will assign to d the value of the product of two doubles.  From the
programmer's point of view, there is no loss of precision.  If
different precision is required, the programmer could use sqrtl or
sqrtf as he or she needed.

So I think that the C++ standard for math should remain in touch with
the developments in the C standard, and it should not use the wrong
tool, overload resolution, to pick the return values of simple math
functions.

Best wishes,

Bob

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Howard Hinnant <hinnant@antispam.metrowerks.com>
Date: Sun, 10 Mar 2002 20:46:29 GMT
Raw View
In article <3c893583$0$24092$4c41069e@reader1.ash.ops.us.uu.net>, P.J.
Plauger <pjp@dinkumware.com> wrote:

| but in C++ we simply
| provide a template function for each math function that Does the Right
| Thing.

If it is a simple template, does it not break the user code I posted
earlier?

#include <cmath>

using namespace std;

struct SmallNum {};

struct BigNum
{
   BigNum() {}
   BigNum(const SmallNum&);
};

BigNum sqrt(const BigNum&);

int main()
{
   SmallNum y;
   BigNum x = sqrt(y);
}

We actually shipped the template solution once and found this out the
hard way.  This demo isn't hypothetical.  The need to restrict the
template to integral (or arithmetic) types only is quite real.

| This is part of the C99/C++ reconciliation proposal we're preparing
| for the next meeting.

Thank you.  I really appreciate you working this issue.

--
Howard Hinnant
Metrowerks

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Gabriel Dos Reis <dosreis@cmla.ens-cachan.fr>
Date: Sun, 10 Mar 2002 20:46:50 GMT
Raw View
Rudy Velthuis <rvelthuis@gmx.de> writes:

| In article <fl8z93kq7v.fsf@jambon.cmla.ens-cachan.fr>, Gabriel Dos Reis
| says...
|
| > | Is there anything that can be done?
| > | Carlos suggested that (at least for std math C functions),  promotion and
| > | overload resolution should work such that 'double' is preferred as the
| > | promoted type so that the overload picks the 'right' function. (right in
| > | terms of common practice)
| >
| > That is a sensible proposal.
|
| But please remember that *preferring* int -> double could result in a
| loss of precision on some FPUs, which could have a double type with a
| mantissa that is smaller than an int.

int -> double is the way C99 handles that issue.  That also is the
historical way that issue has been handled no red herring.

If the user no more than the usual promotion, then he is reponsible to
say so, explicitly.

| E.g. on Pentiums, float is usually
| the same size as an int (4 bytes), so its mantissa is smaller than an
| int, which can result in a loss of least significant bits.

But we're taling of *double*, right?

| The same
| could be true for other CPU/FPU combinations and int -> double.

And presumably there is no guarantee that a long double would have
more precision.  That is a red herring.

--
Gabriel Dos Reis, dosreis@cmla.ens-cachan.fr

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Gabriel Dos Reis <dosreis@cmla.ens-cachan.fr>
Date: Sun, 10 Mar 2002 23:48:51 GMT
Raw View
Howard Hinnant <hinnant@antispam.metrowerks.com> writes:

[...]

| We actually shipped the template solution once and found this out the
| hard way.  This demo isn't hypothetical.  The need to restrict the
| template to integral (or arithmetic) types only is quite real.

I'm wondering why the template solution was retained, where
overloading would have done the job...

(Note that I'm not saying that there is no need for contraining
template-arguments in general)

--
Gabriel Dos Reis, dosreis@cmla.ens-cachan.fr

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "P.J. Plauger" <pjp@dinkumware.com>
Date: Mon, 11 Mar 2002 15:27:33 GMT
Raw View
"Howard Hinnant" <hinnant@antispam.metrowerks.com> wrote in message
news:090320021020403319%hinnant@antispam.metrowerks.com...

> | but in C++ we simply
> | provide a template function for each math function that Does the Right
> | Thing.
>
> If it is a simple template, does it not break the user code I posted
> earlier?

Probably the template function will snag the SmallNum call, rather than
letting the BigNum call win and do the conversion. If that is a worry,
you can a) add an overload for SmallNum, or b) don't include <tgmath.h>.

> We actually shipped the template solution once and found this out the
> hard way.  This demo isn't hypothetical.  The need to restrict the
> template to integral (or arithmetic) types only is quite real.

Or restrict it to <tgmath.h>.

> | This is part of the C99/C++ reconciliation proposal we're preparing
> | for the next meeting.
>
> Thank you.  I really appreciate you working this issue.

Just sent it off to Clark Nelson.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: mseitz@yahoo.com (Matt Seitz)
Date: Mon, 11 Mar 2002 19:03:19 GMT
Raw View
Howard Hinnant <hinnant@antispam.metrowerks.com> wrote in message news:<080320021509369559%hinnant@antispam.metrowerks.com>...
> In article <3C88CA29.2060601@mo.com>, Carlos Moreno
> <moreno_at_mochima_dot_com@mo.com> wrote:
>
> | Then it would be clear that compiler vendors (more specifically, the
> | standard library writers) would be forced to write <cmath> in a way
> | that solevs the problem (there is no need to change the rules of
> | overloading resolution to achieve the above).  One way would be
> | providing overloaded versions for all of the numeric built-in
> | types,
>
> This is the way that Metrowerks solves this problem now.  And like I
> said in a previous posting, it is not a pretty solution.  The header
> that implements this is over 3000 lines of very redundant code.
>
> | or another one using a template:
> |
> | double sqrt(double);
> | long double sqrt(long double);
> | float sqrt(float);
> |
> | template <typename T>
> | double sqrt (T x)
> | {
> |      return sqrt (static_cast<double> (x));
> | }
>
> This solution can break user code.

It certainly will change user code, but I don't know that I would say
that it "breaks" user code.

>Consider:
>
> #include <cmath>
>
> using namespace std;
>
> struct SmallNum {};
>
> struct BigNum
> {
>    BigNum() {}
>    BigNum(const SmallNum&);
> };
>
> BigNum sqrt(const BigNum&);
>
> int main()
> {
>    SmallNum y;
>    BigNum x = sqrt(y);
> }
>
> Today this code works.  y implicitly converts to BigNum and then the
> BigNum overload of sqrt is called.
>
> But if you introduce the templated solution, sqrt(y) will bind to:
>
> double sqrt<SmallNum>(SmallNum);
>
> instead of to:
>
> BigNum sqrt(const BigNum&);

It is not clear to me that binding to "BigNum::sqrt" is right and
binding to "sqrt<SmallNum>" is wrong.  Either seems like a plausibly
correct binding.  If one wants to force the "BigNum::sqrt" binding,
one could always write:
     BigNum x = sqrt(BigNum(y));

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "Fernando Cacciola" <fcacciola@gosierra.com>
Date: Thu, 7 Mar 2002 17:47:04 GMT
Raw View
Hi all, there was a recent discussion in Borland.public.cppbuilder.language,
raised by Carlos Moreno, about the ambiguity that now result from calling
the floating point math functions with integral arguments.

The integral to floating point promotion rules, combined with the overload
resolution rules indicate that the following code is ill-formed:

void f(double);
void f(long double);

int main()
{
  int a = 0 ;

  f(a);  // ambiguity here.

  return 0 ;
}

But this implies a problem that only recently shows up, as compilers do
effectively provide the required overloads for float and long double.

According to the current rules, the 'float'/'long double' overloads for math
functions that used to be only 'double' break a LOT of legacy code that
realized on the default implicit conversion from an integral type to
'double' as a mean to match the correct (unique) math function.

I believe that this issue has not became too much of a problem yet, only
because most compilers only recently started to provide those overloads; but
just as Carlos pointed out, we've been using calls like sqtr(integral_value)
for decades and now they are ambiguous.

Is there anything that can be done?
Carlos suggested that (at least for std math C functions),  promotion and
overload resolution should work such that 'double' is preferred as the
promoted type so that the overload picks the 'right' function. (right in
terms of common practice)


--
Fernando Cacciola
Sierra s.r.l.
fcacciola@gosierra.com
www.gosierra.com


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Howard Hinnant <hinnant@antispam.metrowerks.com>
Date: Thu, 7 Mar 2002 19:57:56 GMT
Raw View
In article <a682f9$ccd8a$1@ID-44132.news.dfncis.de>, Fernando Cacciola
<fcacciola@gosierra.com> wrote:

| I believe that this issue has not became too much of a problem yet, only
| because most compilers only recently started to provide those overloads; but
| just as Carlos pointed out, we've been using calls like sqtr(integral_value)
| for decades and now they are ambiguous.

Metrowerks has provided the overloads for several years now.  And yes,
it has been a royal pain.  We accept sqrt(integral) without ambiguity
(resolves to sqrt(double)), but the solution is in the lib, and it is
ugly (for maintainers).  However customer feedback has demonstrated
this to be one of our most popular extensions, mainly because of legacy
code issues.

| Is there anything that can be done?
| Carlos suggested that (at least for std math C functions),  promotion and
| overload resolution should work such that 'double' is preferred as the
| promoted type so that the overload picks the 'right' function. (right in
| terms of common practice)

I would like to see the concept of "restricted template" introduced
into the language so that this problem can be generally solved (instead
of just solved for the std::math functions).  A restricted template
function would only be considered viable if the argument met a
user-defined compile-time concept or constraint.  For example:

template <typename I : boost::is_integral<I>::value>
inline
double
sqrt(I i)
{
   return sqrt(double(i));
}

The ":" clause following the template parameter must be a compile-time
bool.  If it evaluates to true, everything preceeds normally.  If it
evaluates to false, the function template is not considered viable
among the overloads in scope.

The above syntax is for demonstration only.  The concept of restricted
templates is important.  The syntax I've described it with isn't.

--
Howard Hinnant
Metrowerks

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Gabriel Dos Reis <dosreis@cmla.ens-cachan.fr>
Date: Fri, 8 Mar 2002 17:26:10 GMT
Raw View
"Fernando Cacciola" <fcacciola@gosierra.com> writes:

| Hi all, there was a recent discussion in Borland.public.cppbuilder.language,
| raised by Carlos Moreno, about the ambiguity that now result from calling
| the floating point math functions with integral arguments.

Yes, that is known issue to the Library Working Group; Nico Josuttis
raised soon

[...]

| According to the current rules, the 'float'/'long double' overloads for math
| functions that used to be only 'double' break a LOT of legacy code that
| realized on the default implicit conversion from an integral type to
| 'double' as a mean to match the correct (unique) math function.

Indeed.

[...]

| Is there anything that can be done?
| Carlos suggested that (at least for std math C functions),  promotion and
| overload resolution should work such that 'double' is preferred as the
| promoted type so that the overload picks the 'right' function. (right in
| terms of common practice)

That is a sensible proposal.

--
Gabriel Dos Reis, dosreis@cmla.ens-cachan.fr

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]