Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Re: pow(0, 0)

101 views
Skip to first unread message

Paavo Helde

unread,
Nov 13, 2018, 6:23:41 PM11/13/18
to
On 13.11.2018 23:56, Stefan Ram wrote:
> N4762 literally contains this sentence in N4762 24.5.8p20s2:
>
> |The value returned for pow(0, 0) is implementation-defined.
>
> . »pow« actually is taken over from C. C says:
>
> |A domain error may occur if x is zero and y is zero.
>
> in its section about »pow«. However, C also says in a
> general section:
>
> |If an exceptional condition occurs during the evaluation of
> |an expression (that is, if the result is not mathematically
> |defined or not in the range of representable values for its
> |type), the behavior is undefined.

The above statement is about expressions, not about the math library
function calls. It looks like the C standard makes a difference here.

The C standard contains:

"The behavior of each of the functions in <math.h> is specified
for all representable values of its input arguments, except where
stated otherwise."

"For all functions, a domain error occurs if an input argument is
outside the domain over which the mathematical function is defined."

"On a domain error, the function returns an implementation-defined
value".

So to me it looks like pow(0,0) is already implementation-defined in C
and the C++ standard just repeats that for clarity in the complex pow()
section.

bitrex

unread,
Nov 13, 2018, 7:27:13 PM11/13/18
to
On 11/13/2018 04:56 PM, Stefan Ram wrote:
> N4762 literally contains this sentence in N4762 24.5.8p20s2:
>
> |The value returned for pow(0, 0) is implementation-defined.
>
> . »pow« actually is taken over from C. C says:
>
> |A domain error may occur if x is zero and y is zero.
>
> in its section about »pow«. However, C also says in a
> general section:
>
> |If an exceptional condition occurs during the evaluation of
> |an expression (that is, if the result is not mathematically
> |defined or not in the range of representable values for its
> |type), the behavior is undefined.
>
> IIRC, 0 raised to the power of 0 is not mathematically
> defined. So it should have undefined behavior in C.
>
> Well, actually I am confused because of this:
>
> The quotation
>
> |The value returned for pow(0, 0) is implementation-defined.
>
> stems from a section of C++ that seems to refer to the
> C++ pow function returning a complex value. I.e.,
>
> |N4762 24.5.8 complex transcendentals [complex.transcendentals]
>
> . So does
>
> |The value returned for pow(0, 0) is implementation-defined.
>
> only refer to those overloads of pow that have a complex
> return type?
>
> And if I write
>
> #include <math.h>
> int main(){ pow(0, 0); }
>
> , there seems to be no complex type involved. What can
> we say about »pow( 0, 0 )« in this program, then?
>
> I believe, it has undefined behavior. But N4762 literally
> contains the sentence:
>
> |The value returned for pow(0, 0) is implementation-defined.
>
> . Say what now?
>

std::pow from <cmath> returns 1 for std::pow(base, +0/-0) for any base
including NaN, is it okay to use that?

I can't really think of many mathematical contexts where it makes much
sense to define 0^0 as being something other than 1.

Alf P. Steinbach

unread,
Nov 13, 2018, 7:57:54 PM11/13/18
to
On 14.11.2018 01:27, bitrex wrote:
> [snip]
> I can't really think of many mathematical contexts where it makes much
> sense to define 0^0 as being something other than 1.

If you consider x^n, then

* with x=0 and varying n down from infinity to 1, you get 0 all the way,
and would expect that also when you take n one step further down to 0,

* with n=0 and varying x down from infinity to 1, you get 1 all the way,
and would expect that also when you take x one step further down to 0.

So there are two natural choices for 0^0, namely 0 and 1.

Not sure exactly how x^n varies on the unit circle in the (x,n) plane,
but it must look pretty weird close to (0,0), with that sharp discontinuity.


Cheers!,

- Alf

bitrex

unread,
Nov 14, 2018, 12:26:13 AM11/14/18
to
From a set theory perspective the only choice that makes much sense for
0^0 is 1; the size of the set of all functions from the empty set to the
empty set is 1, the null function.

bitrex

unread,
Nov 14, 2018, 12:48:53 AM11/14/18
to
On 11/13/2018 07:57 PM, Alf P. Steinbach wrote:
AFAIK the analytic _function_ z^n in the complex plane is a different
matter it has a removable singularity at 0^0 defined to be 0 at that
point because all the required directional limits allow it to be removed
a la Riemann's theorem.

<https://en.wikipedia.org/wiki/Removable_singularity>

z^n is holomorphic in C it is everywhere continuous and differentiable
(in C the former implies the latter, but not in R.)

std::pow is not the complex function z^n though it's C++'s equivalent of
the exponentiation operator.

bitrex

unread,
Nov 14, 2018, 1:00:53 AM11/14/18
to
God created the complex numbers where everything works out very nicely
and all else is pathological edge-cases.

"ha ha I wonder when they'll notice that all non-trivial zeros of the
zeta function seem to have real part one half? LOL" God has a sense of humor

Siri Cruise

unread,
Nov 14, 2018, 1:37:33 AM11/14/18
to
In article <0HOGD.580296$VM2.1...@fx04.iad>, bitrex <us...@example.net> wrote:

> > So there are two natural choices for 0^0, namely 0 and 1.

So average them: 0**0 = 1/2

--
:-<> Siri Seal of Disavowal #000-001. Disavowed. Denied. Deleted. @
'I desire mercy, not sacrifice.' /|\
An almond doesn't lactate. This post / \
Yet another supercilious snowflake for justice. insults Islam. Mohammed

Juha Nieminen

unread,
Nov 14, 2018, 3:33:46 AM11/14/18
to
bitrex <us...@example.net> wrote:
> I can't really think of many mathematical contexts where it makes much
> sense to define 0^0 as being something other than 1.

In mathematics, 0^0 is undefined. Period. No ifs, buts or maybes.

In limits, a result of 0^0 is undefined and needs further work to get
the actual answer, which may well be different from 1 (which is the reason
why 0^0 can *not* be said to be 1 even by convention. If you would do that,
you would get wrong answers for limit calculations.)

For example, if

f(x) = exp(-x^-4)
g(x) = x^2

then the limit when x->0 of f(x) is 0, and of g(x) is 0, which means that
the limit when x->0 of f(x)^g(x) is 0^0. Further calculations show that
the actual value of this limit is 0, not 1.

In fact, you can construct limit problems where f(x) and g(x) both
approach 0, and f(x)^g(x) approaches any value you want.

0^0 is not 1. It's undefined.

David Brown

unread,
Nov 14, 2018, 3:54:27 AM11/14/18
to
On 14/11/18 06:26, bitrex wrote:
> On 11/13/2018 07:57 PM, Alf P. Steinbach wrote:
>> On 14.11.2018 01:27, bitrex wrote:
>>> [snip]
>>> I can't really think of many mathematical contexts where it makes
>>> much sense to define 0^0 as being something other than 1.
>>
>> If you consider x^n, then
>>
>> * with x=0 and varying n down from infinity to 1, you get 0 all the way,
>> and would expect that also when you take n one step further down to 0,
>>
>> * with n=0 and varying x down from infinity to 1, you get 1 all the way,
>> and would expect that also when you take x one step further down to 0.
>>
>> So there are two natural choices for 0^0, namely 0 and 1.
>>
>> Not sure exactly how x^n varies on the unit circle in the (x,n) plane,
>> but it must look pretty weird close to (0,0), with that sharp
>> discontinuity.

There is a picture on Wikipedia, as always:

<https://en.wikipedia.org/wiki/Zero_to_the_power_of_zero>

>>
>>
>> Cheers!,
>>
>> - Alf
>
> From a set theory perspective the only choice that makes much sense for
> 0^0 is 1; the size of the set of all functions from the empty set to the
> empty set is 1, the null function.

That would be one way to define it that is consistent with the use of
power for considering the size of sets of functions. But there are
other ways to define it - such as the limit of 0^n as n tends to 0, that
give different values. Indeed, you can pick any non-negative real value
you want and find a continuous path to (0, 0) such that x^y tends
towards your target value.

So there is no choice for 0^0 that lets the power function be continuous
at 0.

However, defining it as 1 is often very useful - for most purposes, it
is either defined to 1 or left undefined.

David Brown

unread,
Nov 14, 2018, 3:56:46 AM11/14/18
to
I thought God created the natural numbers, and all the rest is the work
of man?

Ralf Goertz

unread,
Nov 14, 2018, 4:27:52 AM11/14/18
to
Am Wed, 14 Nov 2018 08:33:35 -0000 (UTC)
schrieb Juha Nieminen <nos...@thanks.invalid>:

> bitrex <us...@example.net> wrote:
> > I can't really think of many mathematical contexts where it makes
> > much sense to define 0^0 as being something other than 1.
>
> In mathematics, 0^0 is undefined. Period. No ifs, buts or maybes.
>
> In limits, a result of 0^0 is undefined and needs further work to get
> the actual answer, which may well be different from 1 (which is the
> reason why 0^0 can *not* be said to be 1 even by convention. If you
> would do that, you would get wrong answers for limit calculations.)
>
> For example, if
>
> f(x) = exp(-x^-4)
> g(x) = x^2
>
> then the limit when x->0 of f(x) is 0, and of g(x) is 0, which means
> that the limit when x->0 of f(x)^g(x) is 0^0. Further calculations
> show that the actual value of this limit is 0, not 1.

Well, that is debatable, isn't it? f(x)^g(x) is the flat function
<https://en.wikipedia.org/wiki/Flat_function> which itself has to be
defined to be 0 at 0 to avoid a division by 0. I know that taking the
limit from both sides you also get 0 and that is infinitely often
differentiable in 0 but still. It is not analytical.

> In fact, you can construct limit problems where f(x) and g(x) both
> approach 0, and f(x)^g(x) approaches any value you want.
>
> 0^0 is not 1. It's undefined.

With that I agree…

bitrex

unread,
Nov 14, 2018, 4:31:02 AM11/14/18
to
On 11/14/2018 03:33 AM, Juha Nieminen wrote:
> bitrex <us...@example.net> wrote:
>> I can't really think of many mathematical contexts where it makes much
>> sense to define 0^0 as being something other than 1.
>
> In mathematics, 0^0 is undefined. Period. No ifs, buts or maybes.

Nonsense it's defined all the time. C++'s std::pow defines it to be 1.
"in mathematics" what's that mean? The One True Mathematics the one God
wrote the book on and handed down?

> In limits, a result of 0^0 is undefined and needs further work to get
> the actual answer, which may well be different from 1 (which is the reason
> why 0^0 can *not* be said to be 1 even by convention. If you would do that,
> you would get wrong answers for limit calculations.)
>
> For example, if
>
> f(x) = exp(-x^-4)
> g(x) = x^2
>
> then the limit when x->0 of f(x) is 0, and of g(x) is 0, which means that
> the limit when x->0 of f(x)^g(x) is 0^0. Further calculations show that
> the actual value of this limit is 0, not 1.
>
> In fact, you can construct limit problems where f(x) and g(x) both
> approach 0, and f(x)^g(x) approaches any value you want.
>
> 0^0 is not 1. It's undefined.
>

It's fascinating material for a real analysis course but defining it to
be "undefined" offers no practical advantages in the context of actually
performing mathematical calculations numerically in a computational
sense other than satisfying a pedant/calculus obsessive. How much real
analysis you do on the computer today?

bitrex

unread,
Nov 14, 2018, 4:38:22 AM11/14/18
to
It's not undefined in set theory. It's not undefined in lambda calculus.
It's undefined in real analysis. The exponential operator is not real
analysis, intrinsically. As if every branch of mathematics is a serf
that somehow has to answer to the Throne of Real Analysis

Juha Nieminen

unread,
Nov 14, 2018, 5:04:54 AM11/14/18
to
bitrex <us...@example.net> wrote:
>> In mathematics, 0^0 is undefined. Period. No ifs, buts or maybes.
>
> Nonsense it's defined all the time. C++'s std::pow defines it to be 1.

If it defines it to be 1, then it's wrong. The only "correct" value that
std::pow(0, 0) could ever return is NaN. Any other value is plain wrong.

You could just as well say that if C++ for some reason defines 0.0/0.0
to be 4.25, then that's how 0/0 is defined to be. Nonsense.

Paavo Helde

unread,
Nov 14, 2018, 5:40:08 AM11/14/18
to
On 14.11.2018 12:04, Juha Nieminen wrote:
> bitrex <us...@example.net> wrote:
>>> In mathematics, 0^0 is undefined. Period. No ifs, buts or maybes.
>>
>> Nonsense it's defined all the time. C++'s std::pow defines it to be 1.
>
> If it defines it to be 1, then it's wrong. The only "correct" value that
> std::pow(0, 0) could ever return is NaN. Any other value is plain wrong.

Unfortunately real compilers do not agree with this.

#include <iostream>
#include <cmath>
#include <complex>

int main() {
auto x = std::pow(0, 0);
auto y = std::pow(std::complex<double>(0, 0), std::complex<double>(0, 0));
std::cout << "x=" << x << ", y=" << y << "\n";
}

Output with MSVC++: x=1, y=(1,0)
Output with gcc: x=1, y=(0,0)


Manfred

unread,
Nov 14, 2018, 7:48:51 AM11/14/18
to
On my box gcc 8.2.1 yields the following:
std::pow(0., 0.) => 1.0;
std::pow(std::complex<double>(0,0), std::complex<double>(0,0)) =>
(nan,nan)

james...@alumni.caltech.edu

unread,
Nov 14, 2018, 8:59:54 AM11/14/18
to
On Wednesday, November 14, 2018 at 4:31:02 AM UTC-5, bitrex wrote:
> On 11/14/2018 03:33 AM, Juha Nieminen wrote:
> > bitrex <us...@example.net> wrote:
> >> I can't really think of many mathematical contexts where it makes much
> >> sense to define 0^0 as being something other than 1.
> >
> > In mathematics, 0^0 is undefined. Period. No ifs, buts or maybes.
>
> Nonsense it's defined all the time. C++'s std::pow defines it to be 1.

Citation, please? The only relevant text I could find has already been
quoted by Stephen Ram, and says that the value is implementation-
defined.

james...@alumni.caltech.edu

unread,
Nov 14, 2018, 9:28:23 AM11/14/18
to
On Wednesday, November 14, 2018 at 5:04:54 AM UTC-5, Juha Nieminen wrote:
> bitrex <us...@example.net> wrote:
> >> In mathematics, 0^0 is undefined. Period. No ifs, buts or maybes.
> >
> > Nonsense it's defined all the time. C++'s std::pow defines it to be 1.

AFAICT, that's incorrect. The only thing that the C++ standard says
about it is that the value is implementation-defined, and it only says
that in the context of the complex version of it.

> If it defines it to be 1, then it's wrong. The only "correct" value that
> std::pow(0, 0) could ever return is NaN. Any other value is plain wrong.

pow() is part of the C standard library that is inherited by C++, with
modifications listed in section C.6. None of the listed modifications
affects this issue. In general, the only thing the C standard says about
pow(0,0) is that "A domain error may occur if x is zero and y is zero."
(7.12.7.4p2). "On a domain error, the function returns an
implementation-defined value; if the integer expression
math_errhandling & MATH_ERRNO is nonzero, the integer expression errno
acquires the value EDOM; if the integer expression
math_errhandling & MATH_ERREXCEPT is nonzero, the ‘‘invalid’’ floating-
point exception is raised." (7.12.1p2)

However, an implementation that pre#defines __STDC_IEC_559__ is required
to also conform to the requirements of Annex F of the C standard, which
are derived from the requirements of ISO/IEC 60559 (== IEEE 754), which
includes, in particular, the requirement that "pow(x, ±0) returns 1 for
any x, even a NaN" (F.10.4.4). So the authors of ISO/IEC 60559
apparently disagree with you. Mathematically, it may be undefined, but
computer math can't be perfectly in accord with the math of real
numbers, and often isn't. I don't know how they chose that rule; one
possibility is that it simplifies implementation of pow().

bitrex

unread,
Nov 14, 2018, 11:01:55 AM11/14/18
to
<https://en.cppreference.com/w/cpp/numeric/math/pow>

"pow(base, ±0) returns 1 for any base, even when base is NaN"

"If base is zero and exp is zero, a domain error may occur."

I assume this to mean that a domain error may or may not occur depending
on implementation, but the operation should still return 1 regardless.
Or that that particular reference is simply "wrong" as well.

It also shows that "pow(+1, NAN) = 1" what the hell does 1 to the power
of NaN even mean, mathematically speaking. But nobody appears to be
wigging out over that one.

bitrex

unread,
Nov 14, 2018, 11:06:23 AM11/14/18
to
There's a bit of an abuse of terminology when we call things like
std::pow or pow "functions." In analysis x^n is a _function_.
Exponentiation is an _operator_.

The value of the _function_ is undefined at 0^0. The behavior of an
_operator_ may be defined as you like.

bitrex

unread,
Nov 14, 2018, 11:15:06 AM11/14/18
to
That is to say the reason why the function x^n at (0, 0) is undefined in
real analysis is because that's what makes the most sense for real
analysis for the exponentiation operator to return, for the reasons
already given. Of course it returns undefined in that situation "you"
set it up that way! Or else one believes in the One True Mathematics
handed down by God in which case I cannot argue anymore.

Ben Bacarisse

unread,
Nov 14, 2018, 11:29:52 AM11/14/18
to
Juha Nieminen <nos...@thanks.invalid> writes:

> bitrex <us...@example.net> wrote:
>> I can't really think of many mathematical contexts where it makes much
>> sense to define 0^0 as being something other than 1.
>
> In mathematics, 0^0 is undefined. Period. No ifs, buts or maybes.

Them's fight'n words! There is a very clear exception when considering
integer powers. 0^0 must be 1 in those cases or almost every bit of
code you write to do combinatorics or symbolic mathematics involving
integer powers (polynomials, series and so one) would have to have
special cases. In fact, you'd fix the special case just once by
defining your own integer power function that gave 0^0 == 1 and you'd
use that instead of the broken built-in one.

This has nothing to do with C++ since C++ has only C's pow function and
its own complex ones. C++ has no integer exponentiation operator or
function to worry about, but I am sure that even you would make

int ipow(int x, int y);

return 1 for ipow(0, 0), wouldn't you?

<snip>
--
Ben.

bitrex

unread,
Nov 14, 2018, 12:07:04 PM11/14/18
to
z^n @ (0, 0) is not undefined in complex analysis either it is 0; z^n
for n in the set of real numbers is a perfectly nice bijective mapping
from the set of complex numbers to the set of complex numbers,
continuous and infinitely differentiable everywhere.

the return value of ::std::complex::pow is unambiguously
implementation-defined however cuz ::std::complex::pow is not the
analytic function z^n.

bitrex

unread,
Nov 14, 2018, 12:14:45 PM11/14/18
to
Nah, natural numbers are just the unit test for complex analysis. If the
result of some operation in complex analysis isn't in agreement with the
behavior of the natural numbers then the problem is in the operation not
elsewhere.

bitrex

unread,
Nov 14, 2018, 12:17:13 PM11/14/18
to
So should actually say they're the base case and the stuff in-between N
and C are the pathological cases.

james...@alumni.caltech.edu

unread,
Nov 14, 2018, 1:09:06 PM11/14/18
to
On Wednesday, November 14, 2018 at 11:06:23 AM UTC-5, bitrex wrote:
> On 11/14/2018 09:28 AM, james...@alumni.caltech.edu wrote:
...
> > pow() is part of the C standard library that is inherited by C++, with
> > modifications listed in section C.6. None of the listed modifications
> > affects this issue. In general, the only thing the C standard says about
> > pow(0,0) is that "A domain error may occur if x is zero and y is zero."
> > (7.12.7.4p2). "On a domain error, the function returns an
> > implementation-defined value; if the integer expression
> > math_errhandling & MATH_ERRNO is nonzero, the integer expression errno
> > acquires the value EDOM; if the integer expression
> > math_errhandling & MATH_ERREXCEPT is nonzero, the ‘‘invalid’’ floating-
> > point exception is raised." (7.12.1p2)
> >
> > However, an implementation that pre#defines __STDC_IEC_559__ is required
> > to also conform to the requirements of Annex F of the C standard, which
> > are derived from the requirements of ISO/IEC 60559 (== IEEE 754), which
> > includes, in particular, the requirement that "pow(x, ±0) returns 1 for
> > any x, even a NaN" (F.10.4.4). So the authors of ISO/IEC 60559
> > apparently disagree with you. Mathematically, it may be undefined, but
> > computer math can't be perfectly in accord with the math of real
> > numbers, and often isn't. I don't know how they chose that rule; one
> > possibility is that it simplifies implementation of pow().
> >
>
> There's a bit of an abuse of terminology when we call things like
> std::pow or pow "functions." In analysis x^n is a _function_.
> Exponentiation is an _operator_.
>
> The value of the _function_ is undefined at 0^0. The behavior of an
> _operator_ may be defined as you like.

The only use of "function" in the text you quoted was from the C++
standard, where the definitions of terms provided by that standard
overrule any other definitions. As a general rule, the reason why any
term is defined in the standard is that it is used there with a meaning
that might be at least subtly (and often not-so-subtly) different from
the meaning it would otherwise have. Such discrepancies are the norm,
not the exception.

C++ Functions are defined in 6.7.2p1, primarily by cross-referece to
9.2.3.5, which explains how to declare them. std::pow() is unambiguously
a C++ function (or more precisely, a family of overloaded functions,
some of them templated).

11.5p1 defines what constitutes a C++ operator; pow isn't on the list.

Still, I was curious to understand the distinction you were making.
While I've formally studied math at a fairly high level, it's been a
long time since I did so, so I reviewed the definitions (marked as
specifically for use in mathematics) provided by wikipedia:

"Formally, a function f from a set X to a set Y is defined by a set G of
ordered pairs (x, y) such that x ∈ X, y ∈ Y, and every element of X is
the first component of exactly one ordered pair in G."
<https://en.wikipedia.org/wiki/Function_(mathematics)#Definition>

"an operator is generally a mapping that acts on elements of a space to
produce other elements of the same space."
<https://en.wikipedia.org/wiki/Operator_(mathematics)>

Each overload of std::pow() has a set X consisting of the outer product
of the set of values represented by it's first parameter's type, and
the set of values representable by it's second parameter's type. It
maps each element of that set to one and only one element of the set Y,
consisting of all representable values of it's return type. Note that
this description applies to any particular implementation of the C/C++
standard library; different implementations might provide slightly
different mappings, due to permissible variations in the accuracy with
which they are implemented.
This seems an excellent match to the definition of a function.

However, X and Y are very different spaces, so it doesn't sound like a
particularly good match to that definition of an operator.

Possibly you're using different definitions for those terms? Nothing
would be more typical of mathematics than the use of different
definitions in different contexts - but by the same token, that should
lead you to respect C++'s decision to provide it's own, overriding
definitions for those terms.

james...@alumni.caltech.edu

unread,
Nov 14, 2018, 1:18:24 PM11/14/18
to
On Wednesday, November 14, 2018 at 11:01:55 AM UTC-5, bitrex wrote:
> On 11/14/2018 08:59 AM, james...@alumni.caltech.edu wrote:
> > On Wednesday, November 14, 2018 at 4:31:02 AM UTC-5, bitrex wrote:
...
> >> Nonsense it's defined all the time. C++'s std::pow defines it to be 1.
> >
> > Citation, please? The only relevant text I could find has already been
> > quoted by Stephen Ram, and says that the value is implementation-
> > defined.
> >
>
> <https://en.cppreference.com/w/cpp/numeric/math/pow>
>
> "pow(base, ±0) returns 1 for any base, even when base is NaN"

That's a requirement stated in the C standard; it's inherited by the C++
standard, but not explicitly stated by it, and it applies only to
implementations which choose to pre#define __STDC_IEC_559__, and only to
the C standard library functions inherited by C++. Rather annoyingly, it
does not apply to the std::pow() overloads for complex types.

bitrex

unread,
Nov 14, 2018, 2:29:10 PM11/14/18
to
Essentially from the definition of a mathematical operation.
Exponentiation is an operation, like +, -, differentiation operator "D",
Laplace transform operator "L", etc. they are not functions.

<https://en.wikipedia.org/wiki/Exponentiation>

x^n is a function. So the question would be what is std::pow supposed to
represent. The formal function x^n as it is used in real analysis, or
the more general mathematical operator of exponentiation, but a
constrained variant of it which operates only on the types it does
simply due to practical limitations of the intrinsic language types i.e.
you're not working with pen and paper

Since the value of the formal function x^n is undefined at (0, 0) as any
real analysis text would say, if that's what they were trying to
represent why would the designers of the language standard allow any
different, in any implementation? it would be wrong! It should just
throw a domain exception no ifs-ands-or-buts. But since they did in fact
do something different that would imply to me that wasn't what they were
trying to make, rather a pragmatically-constrained exponentiation
operator. and " ^ " was already taken.

james...@alumni.caltech.edu

unread,
Nov 14, 2018, 3:53:28 PM11/14/18
to
On Wednesday, November 14, 2018 at 2:29:10 PM UTC-5, bitrex wrote:
> On 11/14/2018 01:08 PM, james...@alumni.caltech.edu wrote:
...
> > Possibly you're using different definitions for those terms? Nothing
> > would be more typical of mathematics than the use of different
> > definitions in different contexts - but by the same token, that should
> > lead you to respect C++'s decision to provide it's own, overriding
> > definitions for those terms.
> >
>
> Essentially from the definition of a mathematical operation.
> Exponentiation is an operation, like +, -, differentiation operator "D",
> Laplace transform operator "L", etc. they are not functions.
>
> <https://en.wikipedia.org/wiki/Exponentiation>

That describes Exponentiation as a mathematical operation, cross-referencing
<https://en.wikipedia.org/wiki/Operation_(mathematics)>, which says "An operation ω is a function of the form ω : V → Y, where V ⊂ X1 × ... × Xk.". Do you agree with that definition? If so, operations are functions, and there's nothing wrong with using the term "function" to refer to an operation.

...
> Since the value of the formal function x^n is undefined at (0, 0) as any
> real analysis text would say, if that's what they were trying to
> represent why would the designers of the language standard allow any
> different, in any implementation?

The answer for the C++ standard is that they were just incorporating the
C standard library by reference. The answer for the C standard is that
they were just following the lead of ISO/IEC 60559. I don't have a copy
of that standard, and even if I did, it wouldn't necessarily explain why
that decision was made. But I do know that in many cases ISO/IEC 60559
just codified existing practice, which often meant following the lead of
Fortran. I'm not sure whether this is one of those cases, but if it is,
you might want to ask why Fortran made that decision. As a general rule,
if there's a discrepancy between the mathematical definition of a
function, and the corresponding computer implementation of that
function, it's generally because it was found to be acceptable in most
cases, and more convenient for at least some cases. That's the best
answer I can give you - I wasn't involved in any of the relevant
decision making.

> it would be wrong! It should just
> throw a domain exception no ifs-ands-or-buts. But since they did in fact

The specification was derived from C, which doesn't have exceptions to
be thrown. C's floating point exceptions (which are incorporated by
reference into C++ - see section 24.4) are very different from C++
exceptions - they don't interrupt the flow of execution, they merely set
flags which can be tested after execution is complete.
In the context of high-performance numerical calculations, it would
often be unacceptable to interrupt the flow of execution with a C++
exception.

> do something different that would imply to me that wasn't what they were
> trying to make, rather a pragmatically-constrained exponentiation
> operator.

That seems extremely plausible. Pragmatism usually plays a big role in the development of standards.

bitrex

unread,
Nov 14, 2018, 5:36:43 PM11/14/18
to
I believe what I should have said is that std::pow(x, y) should probably
be understood to represent the exponential operator, not the operation
of exponentiation itself, and not the real-valued function x^n. Like "^"
but in the development of C++ "^" was already taken for xor and they
didn't give it a new thing. "+" could have been say add(1, 2), okay sure
add(1, 2) will be a function call but "+" operator is not a function. In
say Mathematica "^" means the exponential operator and what it does is
context-dependent on what its operands are.

If it had been given its own symbol in C++ it would've been
over-loadable like any other operator I suppose; that would make sense
as while the concept of "exponentiation" is easy to visualize with
natural numbers as repeated multiplication exponent-number-of-times the
exponentiation operator "^" is used in other contexts where what the
operation it actually performs is is not really at all intuitive, like
you can write A^B where both A and B are matrices.

What does it "mean" to multiply matrix A with itself B times where B is
a rank 47 matrix? I'm not sure I could tell you that from an intuitive
perspective but it can definitely be defined to produce something, as a
valid _operation_, all the way back to the Peano axioms I suppose.

So it's my contention that because of the intrinsic limitations "pow"
while seems to behave a lot like the plain ol' function x^n for floating
point real numbers there's no mathematical law or reason it has to
confirm to the analysis textbook behavior of x^n because that's not
really what it "is."

Just because std::pow can accept floating point real numbers like you're
doing real analysis doesn't mean you have to use it that way, could also
use it to strictly exponentiate values from the set of natural numbers
to do set theory problems, in set theory it's perfectly natural to
define 0^0 = 1 and 0^0 = undefined makes no sense, how come set theory
should get the shaft in deference to what analysis says it should be?

'bout the best argument I can make. :)

Juha Nieminen

unread,
Nov 15, 2018, 6:48:56 AM11/15/18
to
james...@alumni.caltech.edu wrote:
> So the authors of ISO/IEC 60559 apparently disagree with you.

They are free to disagree all they like, but they are still wrong.
0^0 is not 1, never was, and never will be. It's as undefined as,
for example, 0/0, the logarithm of a negative value, the inverse
cosine of a value larger than 1, and so on.

If you make the assumption that 0^0 is 1, you will be getting
incorrect results in, for example, limit problems.

Juha Nieminen

unread,
Nov 15, 2018, 6:56:15 AM11/15/18
to
Ben Bacarisse <ben.u...@bsb.me.uk> wrote:
> I am sure that even you would make
>
> int ipow(int x, int y);
>
> return 1 for ipow(0, 0), wouldn't you?

Why? Zero raised to any power results in zero.
Why is that argument less valid than yours?

Reinhardt Behm

unread,
Nov 15, 2018, 8:57:32 AM11/15/18
to
Any number other than 0 raised the the zeroth power results in 1.

--
Reinhardt

Geoff

unread,
Nov 15, 2018, 9:30:32 AM11/15/18
to
Output of Xcode with clang: x=1, y=(nan,nan)

james...@alumni.caltech.edu

unread,
Nov 15, 2018, 9:31:34 AM11/15/18
to
Not necessarily. Consider a function func(z) that calls pow(x,n)
internally, where x and n depend in some way upon the value of z.
Consider a value of z, call it z0, for which x and n are both 0.
If the standard were changed to specify that pow(0,0) returns a
NaN, as you recommend, then func(z0) will probably also return a
NaN, even if func(z) has a well-defined limit as z approaches z0
from a specified direction. However, there's a pretty good chance
that having pow(0,0) return 1.0, the same value as pow(x,0) for
x!=0.0, will cause func(z0) to return a floating point value that
is exactly the best possible approximation to the true value of
that limit. Whether or not that's true depends upon the function
and the limit. It's easy to construct such a function - the hard
part is proving that such functions come up often enough to
justify this decision. It's certainly not true for all functions
matching my description above.

I don't know for sure, but I wouldn't be surprised to learn that
the decision was originally made (by whoever originally made it -
probably someone responsible for the design of Fortran) based
upon being familiar with a significant number of cases of
practical importance for which that was the case. If so, the
decision may also have been ratified by ISO/IEC 60559, the C
standard, or the C++ standard, by people who were also aware of
those cases. I'd be happier if I knew what those cases were, so I
could cite them for you, but I'd be very surprised if all of
those decisions were made entirely at random, or by someone
unfamiliar with the fact that mathematically, the value of 0^0 is
undefined.

bitrex

unread,
Nov 15, 2018, 9:37:52 AM11/15/18
to
in C++ std::pow is the equivalent of the exponential operator " ^ " in
e.g. Mathematica, they could've added another overloadable operator to
represent exponentiation whose function was context-dependent but it
would've complicated an already complex precedence table so you have the
sort of kludge situation std::pow, std::complex;:pow, you could have
matrix::pow, etc.

Why the return value of regular std::pow for intrinsic types at 0^0 must
be congruent with what real analysis says x^n is at (0, 0) is due to the
behavior of limits is mysterious to me; std::pow is not the analytic
function x^n, and the idea that in all of mathematics in general to
define 0^0 as being anything other than undefined is somehow "wrong" is,
well, simply wrong what it is is context-dependent.

It's irrelevant that defining it to be 1 in set theory or lambda
calculus will give you incorrect results when working with limits in
real analysis, you're not _doing_ real analysis! Gosh!

Shobe, Martin

unread,
Nov 15, 2018, 9:44:41 AM11/15/18
to
On 11/15/2018 5:48 AM, Juha Nieminen wrote:
> james...@alumni.caltech.edu wrote:
>> So the authors of ISO/IEC 60559 apparently disagree with you.
>
> They are free to disagree all they like, but they are still wrong.
> 0^0 is not 1, never was, and never will be. It's as undefined as,
> for example, 0/0, the logarithm of a negative value, the inverse
> cosine of a value larger than 1, and so on.

In combinatorics, 0^0 = 1.
In set theory 0^0 = 1.
In algebra, there's a standard definition for repeated application of an
operator. That definition applied to x^y results in 0^0 = 1.

These are facts that are simply not in dispute as they follow easily
from how x^y is defined in those fields.

Another important fact is that defining 0^0 as 1 doesn't violate the
more important identities of exponentiation. This contrasts with 0/0
where any value you give 0/0 results in one of the field axioms being false.

(BTW, you should look into complex numbers if you want to see what the
logarithm of a negative value is.)

> If you make the assumption that 0^0 is 1, you will be getting
> incorrect results in, for example, limit problems.

You'd be doing limits incorrectly then.

Martin Shobe

james...@alumni.caltech.edu

unread,
Nov 15, 2018, 10:56:19 AM11/15/18
to
On Thursday, November 15, 2018 at 6:48:56 AM UTC-5, Juha Nieminen wrote:
...
> ... It's as undefined as,
> for example, 0/0, the logarithm of a negative value, the inverse
> cosine of a value larger than 1, and so on.

I sorry - I didn't notice those comments until Martin referred to the
second one. You're familiar with complex analysis, and are making those
comments in the context of referring to that fact, yet you list the
logarithm of negative numbers and the arccosine of a value larger than 1
as examples of undefined values? Try calling log(complex(-1.0)) or
acos(complex(2.0)), and tell me what you get (I'm currently in a
location where I can't easily perform the test myself).

Manfred

unread,
Nov 15, 2018, 11:34:05 AM11/15/18
to
On 11/15/2018 3:44 PM, Shobe, Martin wrote:
> In combinatorics, 0^0 = 1.
> In set theory 0^0 = 1.
> In algebra, there's a standard definition for repeated application of an
> operator. That definition applied to x^y results in 0^0 = 1.
>
> These are facts that are simply not in dispute as they follow easily
> from how x^y is defined in those fields.

I am not sure about combinatorics or set theory, but in algebra 0^0 the
above does not apply, as others have pointed out:
n^0 = 1 for each n ∈ ℝ > 0 would yield 0^0 = 1
0^n = 0 for each n ∈ ℝ > 0 would yield 0^0 = 0

So, strictly speaking, 0^0 is a NaN, and in fact this is what the C++
complex library implementation of gcc yields.

That said, it is to be remembered that algebraic analysis is not the
same as numerical analysis, and computers can only do numerical
computations, so it does make sense that in order to support the
expected results some special case of computation yields some
conventional value.

In fact even in algebra the definition of n^0 = 1 is posed to be
consistent with other properties of exponentiation, not because the
definition of exponentiation (i.e. repeated multiplication) shows that.

The fact that some special cases of numerical computation give a
conventional result (and implementation defined) is only to be intended
as "to make things work", and baseline is that it should be handled with
care.
IME such special cases are best avoided, and typically this can be
achieved by a proper rearrangement of the algorithm.

Paavo Helde

unread,
Nov 15, 2018, 3:14:07 PM11/15/18
to
On 15.11.2018 18:33, Manfred wrote:
>
> In fact even in algebra the definition of n^0 = 1 is posed to be
> consistent with other properties of exponentiation, not because the
> definition of exponentiation (i.e. repeated multiplication) shows that.
>
> The fact that some special cases of numerical computation give a
> conventional result (and implementation defined) is only to be intended
> as "to make things work", and baseline is that it should be handled with
> care.
> IME such special cases are best avoided, and typically this can be
> achieved by a proper rearrangement of the algorithm.

Agreed.

On another tangent, when calculating x^n, it might make sense to use an
optimized function for small enough integer arguments, e.g.

int result=1;
for (int i=0; i<n; i++) result*=x;

This will produce 0^0 = 1 in a "natural" way. On the other hand, there
is not much point to provide a special check or branch for 0^n as this
would apply only for invalid/degenerate data which should not be a
normal usage case.

Not sure if this says anything about anything.

Shobe, Martin

unread,
Nov 15, 2018, 4:45:40 PM11/15/18
to
On 11/15/2018 10:33 AM, Manfred wrote:
> On 11/15/2018 3:44 PM, Shobe, Martin wrote:
>> In combinatorics, 0^0 = 1.
>> In set theory 0^0 = 1.
>> In algebra, there's a standard definition for repeated application of
>> an operator. That definition applied to x^y results in 0^0 = 1.
>>
>> These are facts that are simply not in dispute as they follow easily
>> from how x^y is defined in those fields.
>
> I am not sure about combinatorics or set theory, but in algebra 0^0 the
> above does not apply, as others have pointed out:
> n^0 = 1 for each n ∈ ℝ > 0 would yield 0^0 = 1
> 0^n = 0 for each n ∈ ℝ > 0 would yield 0^0 = 0

The case in question is explicitly excluded from the right hand side of
those statements. They say nothing about what 0^0 yields. In other
words, any choice of value for 0^0 is consistent with them.

However, from Wikipedia (yeah, I know, but they were the easiest to find)

"In a monoid, one can define positive integer powers of an element x :
x^1 = x, and x^n = x • ... • x (n times) for n > 1 . The rule of powers
x^(n + p) = x^n • x^p is obvious."

"From the definition of a monoid, one can show that the identity element
e is unique. Then, for any x, one can set x^0 = e and the rule of powers
is still true with nonnegative exponents."

Now, for multiplication, e (the identity element) is 1. So x^0 = 1.

>
> So, strictly speaking, 0^0 is a NaN, and in fact this is what the C++
> complex library implementation of gcc yields.

Strictly speaking 0^0 is whatever the one(s) who defines ^ says it is.
For that library it's NaN. For the cases I mentioned above, it's not.

Martin Shobe

Mr Flibble

unread,
Nov 15, 2018, 4:49:14 PM11/15/18
to
Here's an idea: don't call std::pow(0, 0) as it is a bug.

/Flibble

--
“You won’t burn in hell. But be nice anyway.” – Ricky Gervais

“I see Atheists are fighting and killing each other again, over who
doesn’t believe in any God the most. Oh, no..wait.. that never happens.” –
Ricky Gervais

"Suppose it's all true, and you walk up to the pearly gates, and are
confronted by God," Bryne asked on his show The Meaning of Life. "What
will Stephen Fry say to him, her, or it?"
"I'd say, bone cancer in children? What's that about?" Fry replied.
"How dare you? How dare you create a world to which there is such misery
that is not our fault. It's not right, it's utterly, utterly evil."
"Why should I respect a capricious, mean-minded, stupid God who creates a
world that is so full of injustice and pain. That's what I would say."

Ben Bacarisse

unread,
Nov 15, 2018, 8:54:03 PM11/15/18
to
Beause of the context where ipow would be used. I listed some but I
can't think of any where ipow(0, 0) == 0 is more useful. What sort of
code are you considering?

--
Ben.

Geoff

unread,
Nov 15, 2018, 11:27:39 PM11/15/18
to
On Thu, 15 Nov 2018 06:31:18 -0800 (PST),
james...@alumni.caltech.edu wrote:

>I don't know for sure, but I wouldn't be surprised to learn that
>the decision was originally made (by whoever originally made it -
>probably someone responsible for the design of Fortran) based
>upon being familiar with a significant number of cases of
>practical importance for which that was the case. If so, the
>decision may also have been ratified by ISO/IEC 60559, the C
>standard, or the C++ standard, by people who were also aware of
>those cases. I'd be happier if I knew what those cases were, so I
>could cite them for you, but I'd be very surprised if all of
>those decisions were made entirely at random, or by someone
>unfamiliar with the fact that mathematically, the value of 0^0 is
>undefined.

What did Whitehead and Russell have to say about it in Principia?

Juha Nieminen

unread,
Nov 16, 2018, 7:12:08 AM11/16/18
to
Paavo Helde <myfir...@osa.pri.ee> wrote:
> On another tangent, when calculating x^n, it might make sense to use an
> optimized function for small enough integer arguments, e.g.
>
> int result=1;
> for (int i=0; i<n; i++) result*=x;
>
> This will produce 0^0 = 1 in a "natural" way.

On the other hand, you could optimize the above a bit and say:

int result = x
for (int i = 1; i < n; ++i) result *= x;

which saves one iteration, and which would give you 0 for the case
that both x and n are 0.

Paavo Helde

unread,
Nov 16, 2018, 8:04:39 AM11/16/18
to
This would produce a wrong value x^0 for all x>1.


james...@alumni.caltech.edu

unread,
Nov 16, 2018, 9:32:18 AM11/16/18
to
That will give the wrong result for any value of x other than 1
(except for 0, where the "right" result is not definable).
0 new messages