December 17, 2019
On 17.12.19 14:39, Dominikus Dittes Scherkl wrote:
>>> I think
>> You are wrong.
> Please no ad hominem attacks!
> 
>>
>>> there are only two interesting cases: 

This is not an ad hominem. An ad hominem is dismissing your argument because you were the one who made it. (As a hypothetical example, if I said you were wrong because I did not like the shape of your nose or something like that.)

Aside from not being an ad hominem, the statement above is not even a personal attack. You simply made a wrong statement. There are other interesting cases, therefore your statement was wrong. I have made wrong statements before and I usually just apologize if it happens.
December 17, 2019
On Tuesday, 17 December 2019 at 13:39:13 UTC, Dominikus Dittes Scherkl wrote:
> On Tuesday, 17 December 2019 at 12:31:15 UTC, Timon Gehr wrote:
>>> I still don't understand
>>> why anybody want a function with signature
>>> 
>>> int pow(int, int)
>>> ...
>>
>> You are apparently not aware that std.math.pow is the implementation of the built-in `^^` operator. Removing it is not even on the table, it should just work correctly.
>
> Did I suggest to remove it? NO.
> But I'm of the opinion that having each internal operator returning the same type as the given types is not always useful.
> ^^ should result in a real if the exponent is outside ubyte range.
> Is this wrong? Am I crazy?
>
> Ok, this would be a huge language change, so I agree.
> giving x^^negative_exp == 0 is something we could do.
>
>>> I think
>> You are wrong.
> Please no ad hominem attacks!

That's not an ad hominem. That's just a statement of opinion. It would be ad hominem if something about you was the justification of the wrongness. You're wrong because your name is too long or because you have a big nose etc.



>
>>
>>> there are only two interesting cases:
>>> 
>>> int pow(int, ubyte)
>>>  > and
>>> 
>>> complex pow(complex, complex)
>>> ...
>>
>> Those cases already work fine, and they are off-topic.
> No, they do not work fine, because an implausible input range for the exponent is defined.
> I still think this should be fixed.
>
>> As I am stating for the third time now, there are x such that `pow(x,int.max)` and/or `pow(x,int.min)` neither overflow nor cause any rounding.
> Yes, the cases with x == -1, 0 or 1. And only them.
> Maybe it should be fixed for these cases, for those who insist to use the operator instead of some very fast bit-muggling if they need a toggling function.

It is better that the library function takes care correctly of all cases than requiring all user of a function to insert boilerplate code to handle special cases that are well defined.

>
>>> To make this more prominent for the user, I would only allow 8bit exponents anyway.
>>
>> I am the user in question and I am not a moron. Thanks a lot.
> I didn't say that. Don't lay words into my mouth.
>
>> This is such a trivial issue. This shouldn't be this hard.
> It's not hard. But there are a lot of cases where giving a negative exponent is NOT intended, and maybe it is a better idea to throw to indicate misuse instead of rounding away all information by returning 0? (except for the three special cases where I already agreed treating them correct would be a win).
>
> To summarize:
> If we need to stay with int ^^ int == int, I vote NOT to return 0 for negative exponent and still throw, except for the three special cases, where the correct result should be given.

Except for 0^^0 there is no reason to throw or crap out. int.max+1 also doesn't throw and it is also generally a probable bug in the user code.

December 17, 2019
On 17.12.19 14:39, Dominikus Dittes Scherkl wrote:
> On Tuesday, 17 December 2019 at 12:31:15 UTC, Timon Gehr wrote:
>>> I still don't understand
>>> why anybody want a function with signature
>>>
>>> int pow(int, int)
>>> ...
>>
>> You are apparently not aware that std.math.pow is the implementation of the built-in `^^` operator. Removing it is not even on the table, it should just work correctly.
> 
> Did I suggest to remove it? NO.
> But I'm of the opinion that having each internal operator returning the same type as the given types is not always useful.
> ^^ should result in a real if the exponent is outside ubyte range.
> Is this wrong?

Yes.

> Am I crazy?
> 

Probably not, but note that I am wasting hours of my finite life on what should be a trivial issue with a trivial fix, so I hope you do understand my slight frustration. This is not efficient.

> ...
>>> there are only two interesting cases:
>>>
>>> int pow(int, ubyte)
>>>  > and
>>>
>>> complex pow(complex, complex)
>>> ...
>>
>> Those cases already work fine, and they are off-topic.
> No, they do not work fine,

pow(int,ubyte) works. pow(complex,complex) works.

> because an implausible input range for the exponent is defined.

There are reasonable outputs for the entire range of each individual argument.

> I still think this should be fixed.
> ...

I will argue against this and we will both lose some more time, but feel free to open an enhancement request on bugzilla if you think it is worth it.

>> As I am stating for the third time now, there are x such that `pow(x,int.max)` and/or `pow(x,int.min)` neither overflow nor cause any rounding.
> Yes, the cases with x == -1, 0 or 1. And only them.
> Maybe it should be fixed for these cases, for those who insist to use the operator instead of some very fast bit-muggling if they need a toggling function.
> ...

I don't insist on doing that. I only insist on the function producing correct outputs _when_ it is used. There is no reason to expect it not to work. Also, "very fast bit-muggling" often just does not matter in practice (especially because the compiler developers also know it), it is more typing and it is less readable especially if there is some analogous domain-specific notation.

>>> To make this more prominent for the user, I would only allow 8bit exponents anyway.
>>
>> I am the user in question and I am not a moron. Thanks a lot.
> I didn't say that. Don't lay words into my mouth.
> ...

I didn't do that. In the context of the thread it is however reasonable to interpret your statement as suggesting that I need hand-holding. This is further reinforced by your statement about bit-muggling. (There is a certain kind of smugness that is somewhat common with mediocre programmers, and your statements will resonate with the afflicted, even if you didn't intend them that way.)

>> This is such a trivial issue. This shouldn't be this hard.
> It's not hard. But there are a lot of cases where giving a negative exponent is NOT intended, and maybe it is a better idea to throw to indicate misuse instead of rounding away all information by returning 0?

I'd highly prefer it to consistently return 0 rather than kill my program without a stack trace. However, I agree that this case is slightly less clear-cut.

> (except for the three special cases where I already agreed treating them correct would be a win).
> ...

(After I repeated my explanation for the third time.)
December 17, 2019
On 17.12.19 15:02, Patrick Schluter wrote:
> 
> Except for 0^^0 there is no reason to throw or crap out.

0^^0 = 1 and D gets this right already.
December 17, 2019
On 17.12.19 15:29, Timon Gehr wrote:
> On 17.12.19 15:02, Patrick Schluter wrote:
>>
>> Except for 0^^0 there is no reason to throw or crap out.
> 
> 0^^0 = 1 and D gets this right already.

Also, 0^^x for x<0 is _actually_ a division by zero.
December 17, 2019
On Tuesday, 17 December 2019 at 14:26:42 UTC, Timon Gehr wrote:
> On 17.12.19 14:39, Dominikus Dittes Scherkl wrote:
>> ^^ should result in a real if the exponent is outside ubyte range.
>> Is this wrong?
>
> Yes.
Why? It's highly likely that the result is out of range or is a fraction.
A floatginpoint value would give much more useful information.

> There are reasonable outputs for the entire range of each individual argument.

If you look at it from mathematical point of view:
pow is defined as a mapping
ℕ × ℕ → ℕ
but if we extend it to
ℤ × ℤ → ℤ
we have undefined points, because 1/n is not in ℤ for all n>1.
Maybe it is convenient to return 0 in such cases, but not correct.
Throwing is more plausible, if restricting to ℤ × ℕ → ℤ or
extending to ℤ × ℤ → ℚ is wrong as you state above.

> I'd highly prefer it to consistently return 0 rather than kill my program without a stack trace. However, I agree that this case is slightly less clear-cut.
At least it is not what I would call "consistent".

Meanwhile I looked at the implementation and it has more places to optimize.
E.g. it should give 1 for any value x^^0, not only for 0^^0 (nothing to calc here)
also all comments talk about n for the exponent, while in fact m is used in the code.

December 17, 2019
On 17.12.19 15:02, Patrick Schluter wrote:
>>>>
>>> You are wrong.
>> Please no ad hominem attacks!
> 
> That's not an ad hominem. That's just a statement of opinion. It would be ad hominem if something about you was the justification of the wrongness. You're wrong because your name is too long or because you have a big nose etc.

Although some people do count disagreeing with mathematically wrong statements as a personal attack:

https://github.com/pandas-dev/pandas/issues/9422#issuecomment-343550192

I think this is not a useful way to run a community.

Note that the provided "proof" is not entirely right, the mathematical justification for sum([])=0 is actually this: https://en.wikipedia.org/wiki/Free_monoid#Morphisms
December 17, 2019
On 17.12.19 16:07, Dominikus Dittes Scherkl wrote:
> On Tuesday, 17 December 2019 at 14:26:42 UTC, Timon Gehr wrote:
>> On 17.12.19 14:39, Dominikus Dittes Scherkl wrote:
>>> ^^ should result in a real if the exponent is outside ubyte range.
>>> Is this wrong?
>>
>> Yes.
> Why?

It's neither sound nor complete, you can't even enforce it in a precise manner and it defies expectations.

Let's say I write:

auto foo(T...)(T args){
    // ...
    return a^^(b?2:3)
}

Someone comes along that has a dislike for ternary operators and my formatting preferences and refactors the code to the obviously equivalent

auto foo(T...)(T args)
{
    // ... (rename "a" to "base")
    int exponent; // typeof(2) = typeof(3) = int
    if (b)
    {
        exponent = 2;
    }
    else
    {
        exponent = 3;
    }
    return base^^exponent;
}

Now suddenly my program starts to implicitly use `real` computations all over the place, potentially creating wrong outputs and producing different results on different machines.

Thanks, but no. Emphatically. This is a terrible idea.

It's highly likely that the result is out of range or is a fraction.
> A floatginpoint value would give much more useful information.
> 
>> There are reasonable outputs for the entire range of each individual argument.
> 
> If you look at it from mathematical point of view:
> pow is defined as a mapping
> ℕ × ℕ → ℕ
> but if we extend it to
> ℤ × ℤ → ℤ

There's no such thing.

> we have undefined points, because 1/n is not in ℤ for all n>1.

Or for n=0.

> Maybe it is convenient to return 0 in such cases, but not correct.
> Throwing is more plausible, if restricting to ℤ × ℕ → ℤ or
> extending to ℤ × ℤ → ℚ is wrong as you state above.
> ...

uint is not ℕ, int is not ℤ, real is not ℚ, and unsigned types don't help prevent programming errors (the opposite is the case). Also, you can't extend xʸ to ℤ×ℤ→ℚ.

>> I'd highly prefer it to consistently return 0 rather than kill my program without a stack trace. However, I agree that this case is slightly less clear-cut.
> At least it is not what I would call "consistent".
> ...

x⁻¹ = 1/x. 1/2 = 0. 2^^-1 = 0. Consistent.

The interpretation ⟦.⟧ of integer division admits:

⟦a/b⟧=truncate(⟦a⟧/⟦b⟧) in case (⟦a⟧,⟦b⟧) and truncate(⟦a⟧/⟦b⟧) are in range.

Therefore it is consistent to say:

⟦a^^b⟧=truncate(⟦a⟧^^⟦b⟧) in case (⟦a⟧,⟦b⟧) and truncate(⟦a⟧^^⟦b⟧) are in range.

December 17, 2019
On Tuesday, 17 December 2019 at 14:29:58 UTC, Timon Gehr wrote:
> On 17.12.19 15:02, Patrick Schluter wrote:
>> 
>> Except for 0^^0 there is no reason to throw or crap out.
>
> 0^^0 = 1 and D gets this right already.

Not as clear cut as you say, but generally it is agreed upon being set to 1.

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

but to clarify what I meant

except for 0^0 where an exception could be justified, all other cases have no reason to even contemplate throwing an exception. It is not mathematically justified. If D was a managed language where integer overflows are handled by default it would be another story. That's why I consider that your position is the right one.
December 17, 2019
On Tuesday, 17 December 2019 at 15:08:31 UTC, Timon Gehr wrote:
> On 17.12.19 15:02, Patrick Schluter wrote:
>>>>>
>>>> You are wrong.
>>> Please no ad hominem attacks!
>> 
>> That's not an ad hominem. That's just a statement of opinion. It would be ad hominem if something about you was the justification of the wrongness. You're wrong because your name is too long or because you have a big nose etc.
>
> Although some people do count disagreeing with mathematically wrong statements as a personal attack:

Just quoting "I think" with a reply "you are wrong" isn't any better way to run a community. It's not so much that you are disagreeing with it, so much as you don't know how to convey that you are disagreeing with it like a human being.

> https://github.com/pandas-dev/pandas/issues/9422#issuecomment-343550192

So you had to pull an example from close to 3 years ago?