Your observations are all correct. The behaviour you see are because GNU APL values can be either integers (backed by a 64-bit integral value) or floating point (backed by a C++
double value). Conversions between these two underlying storage formats are transparent to the user most of the time.
I know you are probably aware of most of the following, but I'm just summarising to ensure that we're all on the same page:
This conversion can come back and bite you when you are doing arithmetic in the border region between 2^53 and 2^63. Look at this somewhat unexpected behaviour for example:
y ← 2⋆62
y
4611686018427387904
y+1
4611686018427387905 ⍝ OK, accurate
y+1.1
4611686018427387904 ⍝ Add a larger value but get a smaller result?
y+10.1
4611686018427387904 ⍝ OK, this seems really weird
The reason for this is of course that as soon as you introduce non-integral numbers, the implementation falls back to floating point. Common Lisp does exactly the same thing for this example by the way.
Now, as for the actual issue you are observing, I believe I have a good explanation of what you are seeing. Let's look at the implementation of decode.
We can see in the code that the actual computation is being done with values of type APL_Complex. This type has the following definition:
typedef std::complex<double> APL_Complex;
Thus, the actual calculation is being performed with C++ double values.
After the computation is done, the value is converted back to either an APL_Float or APL_Integer depending on the magnitude of the value.
Finally, here's the code for the decode function for reference:
Bif_F12_DECODE::decode(Cell * cZ, ShapeItem len_A, const Cell * cA,
ShapeItem len_B, const Cell * cB, ShapeItem dB,
double qct)
{
APL_Complex value(0.0, 0.0);
APL_Complex weight(1.0, 0.0);
uint32_t len = len_A == 1 ? len_B : len_A;
cA += len_A - 1; // point to the lowest item in A
cB += dB*(len_B - 1); // point to the lowest item in B
loop(l, len)
{
value += weight*cB[0].get_complex_value();
weight *= cA[0].get_complex_value();
if (len_A != 1) --cA;
if (len_B != 1) cB -= dB;
}
if (value.imag() > qct)
new (cZ) ComplexCell(value);
else if (value.imag() < -qct)
new (cZ) ComplexCell(value);
else if (Cell::is_near_int(value.real(), qct))
new (cZ) IntCell(value.real(), qct);
else
new (cZ) FloatCell(value.real());
}
Regards,
Elias