Mailing List Archive

quadmath with 32 bit ints
So that I can torture-test some of my code I've tried to build perls
with all the various combinations of integer and float sizes. And I've
come across a problem with 32-bit integers and quadruple-precsion floats
via libquadmath.

On a 32-bit Linux, perl builds just fine with the following Configure
options and passes all its tests:

-de
-de -Duselongdouble
-de -Duse64bitint
-de -Duse64bitint -Duselongdouble
-de -Duse64bitint -Dusequadmath

It builds, but has test failures with:

-de -Dusequadmath

I've tried with 5.32.0 and 5.34.0 and get exactly the same test failures
on both:

Failed 10 tests out of 2449, 99.59% okay.
../cpan/Math-BigInt-FastCalc/t/bigintfc.t
../cpan/Math-BigInt/t/bare_mbf.t
../cpan/Math-BigInt/t/bare_mbi.t
../cpan/Math-BigInt/t/bigfltpm.t
../cpan/Math-BigInt/t/bigintc.t
../cpan/Math-BigInt/t/bigintpm.t
../cpan/Math-BigInt/t/sub_mbf.t
../cpan/Math-BigInt/t/sub_mbi.t
../cpan/Math-BigInt/t/upgrade.t
../cpan/Math-BigInt/t/with_sub.t

That inclines me to think that the bug is *probably* in Math::BigInt,
but I suppose there's a possibility that that just happens to be the
only place in the tests that tickles a bug in perl itself.

Before I go digging into this, has anyone seen this before? Is anyone
familiar enough with BigInt to know what the problem is?

--
David Cantrell
Re: quadmath with 32 bit ints [ In reply to ]
On Wed, Jun 9, 2021 at 6:50 PM David Cantrell <david@cantrell.org.uk> wrote:
[snip]

> Before I go digging into this, has anyone seen this before?
>

Yes, I think so:
https://github.com/Perl/perl5/issues/18533

My preferred (simplistic) patch to Calc.pm, which I eventually presented
at https://github.com/pjacklam/p5-Math-BigInt/pull/1 (and which I've been
using successfully for the last few months) is:
#########
--- Calc.pm_orig 2021-01-23 16:23:26 +1100
+++ Calc.pm 2021-01-23 16:30:19 +1100
@@ -147,6 +147,12 @@
}
}

+ # The above procedure leads to some test failures on perls where
ivsize is 4
+ # && nvtype is __float128. Here, for that configuration only, we hard
code
+ # in values known to work.
+ use Config;
+ if($Config{ivsize} == 4 && $Config{nvtype} eq '__float128') { $e = 9;
$int = 0; }
+
__PACKAGE__ -> _base_len($e, $int); # set and store

use integer;
########

Thanks for raising !!
I just get ignored ... though there might be good reasons for that ...

Cheers,
Rob
Re: quadmath with 32 bit ints [ In reply to ]
On 09/06/2021 10:50, sisyphus wrote:
> David Cantrell <david@cantrell.org.uk> wrote:
> > Before I go digging into this, has anyone seen this before?
> Yes, I think so:
> https://github.com/Perl/perl5/issues/18533
> <https://github.com/Perl/perl5/issues/18533>
>
> My preferred (simplistic) patch to Calc.pm, which I eventually presented
> at https://github.com/pjacklam/p5-Math-BigInt/pull/1

I can confirm that that fixes it on Linux too. Thanks!

--
David Cantrell
Re: quadmath with 32 bit ints [ In reply to ]
Hi

I saw the patch, but I wanted to see if I could modify the existing logic
to handle this case too, rather than making this a special case. It turned
out to be not as easy as I expected. Since then, not much has happened. The
patch is certainly better than the current situation, so I'll shift my
priorities and get it included.

Cheers,
Peter
Re: quadmath with 32 bit ints [ In reply to ]
On Thu, Jun 10, 2021 at 4:48 PM Peter John Acklam <pjacklam@gmail.com>
wrote:

I saw the patch, but I wanted to see if I could modify the existing logic
> to handle this case too, rather than making this a special case.
>

I also spent some time trying (unsuccessfully) to have the existing logic
handle this case.
Eventually, I decided that, given the rarity of perls with 4-byte IVs and
16-byte NVs, we could get away with simply nominating values of $e and $int
that work.

For this perl configuration, using this particular patch, we still work our
way through that "existing logic" but then simply discard the result of it.
If we're going to discard the result, it would be smarter to not work
through the "existing logic" in the first place.
However, I think the savings to be made by being "smarter" in this way
would be immeasurably minute anyway ... so I'll leave it up to you, Peter,
as to how much trouble you want to go to.

Cheers,
Rob
Re: quadmath with 32 bit ints [ In reply to ]
tor. 10. jun. 2021 kl. 16:21 skrev sisyphus <sisyphus359@gmail.com>:
>
> I also spent some time trying (unsuccessfully) to have the existing logic handle this case.
> Eventually, I decided that, given the rarity of perls with 4-byte IVs and 16-byte NVs, we could
> get away with simply nominating values of $e and $int that work.

I got something working. It stops with $e = 16 and $use_int = 0. Even
with $e = 17, all the values are within the range of what can be
represented exactly with 128 bit floats, but certain things fail. For
instance, I expected the following to return 1

$ perl -wle 'print( (2**113 - 1) % 2)'
0

I guess it has to do with how the % operator is implemented. I haven't
dug into it. POSIX::fmod() gets it right, though

$ perl -MPOSIX -wle 'print fmod(2**113 - 1, 2)'
1

but I don't think it's worth requiring the POSIX module just to get $e
= 17 rather than $e = 16.

Peter
Re: quadmath with 32 bit ints [ In reply to ]
On Fri, Jun 11, 2021 at 4:46 PM Peter John Acklam <pjacklam@gmail.com>
wrote:

I got something working. It stops with $e = 16 and $use_int = 0. Even
> with $e = 17, all the values are within the range of what can be
> represented exactly with 128 bit floats, but certain things fail. For
> instance, I expected the following to return 1
>
> $ perl -wle 'print( (2**113 - 1) % 2)'
> 0
>
> I guess it has to do with how the % operator is implemented. I haven't
> dug into it. POSIX::fmod() gets it right, though
>
> $ perl -MPOSIX -wle 'print fmod(2**113 - 1, 2)'
> 1
>
> but I don't think it's worth requiring the POSIX module just to get $e
> = 17 rather than $e = 16.
>

I notice that a comparable anomaly arises when ivsize == 4 and nvtype is
the 64-bit precision long double (nvsize == 12).:

C:\>perl -le "print ( (2**64 - 1) % 2);"
0

C:\>perl -MPOSIX -le "print fmod(2**64 - 1, 2);"
1

Yet, for this configuration, there's no problem with the existing Calc.pm
code.

Why is it that the problem arises only when ivsize == 4 && nvsize == 16 ?

Cheers,
Rob

PS Based on the above I expected that, on a perl whose ivsize == 4 &&
nvsize == 8, the expression '"print ((2** 53 - 1) % 2);' would also output
0 ... but it doesn't:

C:\>perl -MConfig -le "print $Config{ivsize}; print $Config{nvsize}; print
( (2**53 - 1) % 2);"
4
8
1

This is not making sense to me.
If a perl whose ivsize == 4 && nvsize == 8 determines that (2**53 - 1) % 2
== 1 then, by extrapolation:
1) a perl whose ivsize == 4 && nvsize == 12 should determine that (2** 64
- 1) % 2 == 1
&&
2) a perl whose ivsize == 4 && nvsize == 16 should determine that (2** 113
- 1) % 2 == 1

But points 1) and 2) are not being met.
Am I missing something ?
Re: quadmath with 32 bit ints [ In reply to ]
lør. 12. jun. 2021 kl. 14:44 skrev sisyphus <sisyphus359@gmail.com>:
>
> This is not making sense to me.
> If a perl whose ivsize == 4 && nvsize == 8 determines that (2**53 - 1) % 2 == 1 then, by extrapolation:
> 1) a perl whose ivsize == 4 && nvsize == 12 should determine that (2** 64 - 1) % 2 == 1
> &&
> 2) a perl whose ivsize == 4 && nvsize == 16 should determine that (2** 113 - 1) % 2 == 1
>
> But points 1) and 2) are not being met.
> Am I missing something ?

Just for fun I compiled Perl 5.32.1 with ivsize == 4 and nvsize == 16
on a Solaris 10 host. I get the same result, i.e., (2**112-1) % 2 is
1, but (2**113-1) % 2 is 0. I also get that POSIX::fmod(2**113, 2) is
1.

Peter
Re: quadmath with 32 bit ints [ In reply to ]
I've just today filed a bug report (
https://github.com/Perl/perl5/issues/18889 ) about this questionable
behaviour of the % operator.
No responses to it yet.

Cheers,
Rob
Re: quadmath with 32 bit ints [ In reply to ]
An update …

I modified the logic so it can handle the case with ivsize = 4 and
nvsize = 16. However, it broke Math::BigInt::FastCalc, which uses the
same base length as Math::BigInt::Calc. There is something in the XS
code that causes Math::BigInt::FastCalc to fail when the base length
is larger than 15. I don't know what causes the problem. To make
things worse, a base length of 15 doesn't work on systems with ivsize
= 4. It would work on systems with ivsize = 4 if it hadn't been for
the _is_even() and _is_odd() methods. They convert the NV to a UV and
do a bitwise "… & 1" to determine if a number is odd or even.
Consequently, we can't use NVs that are too large to fit inside a UV.

I believe it would be hardly noticeable if I scrapped the _is_even()
and _is_odd() in Math::BigInt::FastCalc. If I did, then
Math::BigInt::FastCalc would work with a base length of 15 also when
the ivsize = 4, but I don't know how much there is to gain from
increasing the base length from 9 to 15. I haven't timed it.

When that was working, I thought it would be useful to be able to
specify the base length manually. There is an RT with a wish for this
functionality. It turned out to be a lot more work then I expected,
because Math::BigInt::Calc makes a lot of assumptions on the minimum
base length. After some work, I got it working, so with my development
version of Math::BigInt, I can explicitly set the base length to 3
with

use Math::BigInt::Calc base_len => 3;
use Math::BigInt lib => "Calc";

Hopefully, I will be able to release something in the not too distant future.

Peter