Subject: | minmax truncates intermediates to double |
minmax uses type 'double' to hold array values for comparison. This loses precision on 64-bit machines. E.g.:
perl -MList::MoreUtils=minmax -E 'my @n = map { ~0-$_ } 0..10; say join " ", minmax(@n);'
18446744073709551615 18446744073709551615
When the type is changed to NV and run on a Perl with long doubles, we get:
18446744073709551605 18446744073709551615
as expected.
List::Util min/max uses an NV, so with long doubles it gives the right answer:
perl -MList::MoreUtils=minmax -MList::Util=min,max -E 'my @n = map { ~0-$_ } 0..10; say join " ", minmax(@n); say join " ", min(@n), max(@n)'
18446744073709551615 18446744073709551615
18446744073709551605 18446744073709551615
However this doesn't work on "normal" Perl builds which give the incorrect:
perl -MList::MoreUtils=minmax -MList::Util=min,max -E 'my @n = map { ~0-$_ } 0..10; say join " ", minmax(@n); say join " ", min(@n), max(@n)'
18446744073709551615 18446744073709551615
18446744073709551615 18446744073709551605
However it does give the option of using objects (e.g. Math::BigInt, Math::GMPz, Math::GMP) to work around. Change to "my @n = map { Math::BigInt->new($_) } map { ~0-$_ } 0..10" for example, and min/max will work.
Ideas:
1. Switch to NV, call it a day. 64-bit integer comparisons will be broken on most machines and we don't care. Let namby-pamby number theory people go use Python.
2. Do object comparisons when we see them like in List::Util. At least we can work around it.
3. Instead of putting everything in a NV, use SvIsUV etc. to keep IV/UV as IV/UV, so no data loss. This is complicated (especially when objects are thrown in) and one still has to solve the mixed cases (e.g. an array containing a mix of IV, UV, NV, objects) which may have no perfect answer.