Topic: Hidden danger in numeric_limits


Author: ncm@nospam.cantrip.org (Nathan Myers)
Date: 1999/04/27
Raw View
Joe Buck<jbuck@synopsys.com> wrote:
>ncm@nospam.cantrip.org (Nathan Myers) writes:
>>somebody wrote:
>>>The default definitions of max() and min()
>>>are ridiculous, so why even have them?
>>
>>Of course this alternative was considered.  The defaults are
>>there so that you can have conditional statements in a function
>>and use the nonsensical members in dead code without getting
>>compile errors:
>
>Then perhaps the nonsensical default members should throw an exception
>if called.  That way calls to them in dead branches still compiles.
>Code like your example would still work:
>
>>  if (std::numeric_limits<T>::is_float) {
>>    // use mantissa-related members
>>  } else {
>>    // use non-mantissa-related members
>>  }
>
>If the user accidentally reversed the conditional, s/he would get
>an immediate exception.

This seems like a reasonable idea.  I don't recall anybody suggesting it.
Of course it wouldn't help with the manifest-constant values.

--
Nathan Myers
ncm@nospam.cantrip.org  http://www.cantrip.org/
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: jbuck@synopsys.com (Joe Buck)
Date: 1999/04/25
Raw View
ncm@nospam.cantrip.org (Nathan Myers) writes:
>>This is good thinking.  The default definitions of max() and min()
>>are ridiculous, so why even have them?
>
>Of course this alternative was considered.  The defaults are
>there so that you can have conditional statements in a function
>and use the nonsensical members in dead code without getting
>compile errors:

Then perhaps the nonsensical default members should throw an exception
if called.  That way calls to them in dead branches still compiles.
Code like your example would still work:

>  if (std::numeric_limits<T>::is_float) {
>    // use mantissa-related members
>  } else {
>    // use non-mantissa-related members
>  }

If the user accidentally reversed the conditional, s/he would get
an immediate exception.

--
The reasonable man adapts himself to the world; the unreasonable man
persists in trying to adapt the world to himself. Therefore, all progress
depends on the unreasonable man.         -----George Bernard Shaw
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: ncm@nospam.cantrip.org (Nathan Myers)
Date: 1999/04/22
Raw View
Siemel Naran<sbnaran@KILL.uiuc.edu> wrote:
>On 20 Apr 1999 21:19:02 GMT, Ed Brey <brey@afd.mke.etn.com> wrote:
>>I propose that a safe_numeric_limits<T> class be added which contains only
>>meaningful members.
>This is good thinking.  The default definitions of max() and min()
>are ridiculous, so why even have them?

Of course this alternative was considered.  The defaults are
there so that you can have conditional statements in a function
and use the nonsensical members in dead code without getting
compile errors:

  if (std::numeric_limits<T>::is_float) {
    // use mantissa-related members
  } else {
    // use non-mantissa-related members
  }

The alternative would require specialized versions of tiny parts
of useful functions.  You can arrange to get a compile-time warning
of not-explicitly specialized instantiations by having functions
use or return "check(v)" rather than "v", where check is defined:

  template <class T, bool I = std::numeric_limits<T>::is_specialized>
    T check(const T&);  // not defined
  template <class T>
    inline T check<T,true>(const T& t) { return t; }

This should cost nothing at run time.

--
Nathan Myers
ncm@nospam.cantrip.org  http://www.cantrip.org/



[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Ed Brey" <brey@afd.mke.etn.com>
Date: 1999/04/20
Raw View
The requirement "The default numeric_limits<T> template shall have all
members, but with 0 or false values." [18.2.1.1] is dangerous.

An example:

typedef int MyType;

void safe_increment(MyType& i) {
    if (i == std::numeric_limits<MyType>::max())
        throw std::exception("Too big");
    ++i;
}

This is fine, but now lets say that we're particularily concerned about the
size (number of bits allocated) of MyType, because of space or speed
requirements, or because we are passing through some interface that requires
a certain size.  This kind of concern is what causes programmers to typedef
built-in types in the first place: MyType can exist once in a standard
header file and be used throughout the program without caring about its size
(as long as precautions such as the above are used).

The problem comes in if we have a desire to get a little non-standard.
Maybe none of char, int, and long fit the bill for our size, but we happen
to be on a platform that supports something such as __int64 for a 64-bit
integer.  Or perhaps we need to compile on two platforms that have different
size shorts, and we always want a 16-bit integer, and both platforms happen
to define __int16, so we use that so that we don't need any
compiler-specific code.

We set our typedef and happily recompile away.  Everything works.  Or so it
seems.  It turns out that our std::numeric_limits<MyType>::max() now is
always returning 0, although we get no warning that this is the case,
creating a nasty hidden bug.

The problem is that there exists non-standard fundimental types that one
would reasonable expect to have meaningful values for
std::numeric_limits<MyType>::max(), especially if it is the same size as an
existing type (even though the compiler sees it as its own type for the
purposes of specialization).  Since the standard library may be obtained
separately from the compiler, it may not know about the non-standard types.
(Even standard libraries shipped with the compiler may be so naive (e.g.
VC++)).

There are ways that programmers can protect themselves:
- Don't use non-standard fundamental types.
- Always check is_specialized before using a numeric_limits function.

Neither of these workaround are practical.  In the latter case, this would
require checking is_specialized before using a numeric_limits function for
any typedefed type, since one never knows when it will be assigned to a
non-standard type.

I propose that a safe_numeric_limits<T> class be added which contains only
meaningful members.  The non-specialized instantiation would contain only
is_specialized and other such flags.  Of course, a better than than
afe_numeric_limits would be preferable, but it should be short and obvious,
as the safe class should always be used unless the program requires and can
deal with the return values of non-meaningful member functions (for example
if their values are being written into a table but will be ignored because
of the data type).



[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: sbnaran@bardeen.ceg.uiuc.edu (Siemel Naran)
Date: 1999/04/21
Raw View
On 20 Apr 1999 21:19:02 GMT, Ed Brey <brey@afd.mke.etn.com> wrote:

>We set our typedef and happily recompile away.  Everything works.  Or so it
>seems.  It turns out that our std::numeric_limits<MyType>::max() now is
>always returning 0, although we get no warning that this is the case,
>creating a nasty hidden bug.

>I propose that a safe_numeric_limits<T> class be added which contains only
>meaningful members.  The non-specialized instantiation would contain only
>is_specialized and other such flags.  Of course, a better than than
>afe_numeric_limits would be preferable, but it should be short and obvious,
>as the safe class should always be used unless the program requires and can
>deal with the return values of non-meaningful member functions (for example
>if their values are being written into a table but will be ignored because
>of the data type).

Let me check if I understand your argument.  Instead of
   template <class T>
   struct numeric_limits {
      T max() { return T(); };
      T min() { return T(); };
   };
we should use
   template <class T>
   struct numeric_limits { };

This is good thinking.  The default definitions of max() and min()
are ridiculous, so why even have them?

In any case, the coding guideline is that if you write a new
numerical type, like class int256, then you should provide a
specialization of std::numeric_limits<int256>.

Finally, I've noticed that the generic char_traits<T> also has lots
of junk:
   template <class Ch>
   struct char_traits {
      typedef Ch int_type;
      int_type eof() { return int_type(-1); }
   };
But the specialization is useful:
   template <>
   struct char_traits<char> {
      typedef int int_type;
      int_type eof() { return int_type(-1); }
   };
Why not leave the generic version blank?

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]