issue 7006 - std.math.pow (integral, integral) crashes on negative exponents

Timon Gehr timon.gehr at gmx.ch
Tue Dec 17 15:40:03 UTC 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.



More information about the Digitalmars-d mailing list