I've given it a run and it looks pretty good here.
I came across one interesting little anomaly:
############################
use strict;
use warnings;
use Math::BigRat;
use Test::More;
use Inline C => <<'EOC';
int is_eq(SV * n1, SV * n2) {
if(sv_numeq(n1, n2)) return 1;
return 0;
}
EOC
my $bigq1 = Math::BigRat->new(
'3602879701896397/36028797018963968'
);
my $bigq2 = Math::BigRat->new(
'1/10'
);
cmp_ok($bigq1, '!=', $bigq2, "Rationals are unequal");
cmp_ok(is_eq($bigq1, $bigq2), '!=', 1, "sv_numeq detects inequality");
done_testing
#############################
This outputs:
ok 1 - Rationals are unequal
not ok 2 - sv_numeq detects inequality
# Failed test 'sv_numeq detects inequality'
# at sv_numeq.pl line 23.
# got: 1
# expected: anything else
Both of those rationals equate to the double 0.1. (The first is an exact
rational representation of the value contained in that double; the second
rational rounds to that same double.)
I don't know if anything could be done about that.
On investigation, it seems that sv_numeq() is performing the division
that's implicit in the rationals, resulting in 2 doubles that contain the
same value (0.1). Unsurprisingly it therefore returns true.
2 additional tests (based on my initial, somewhat hopeful, expectations)
that I ran were:
cmp_ok(is_eq(0.1, $bigq1), '==', 1, "sv_numeq agrees that 0.1 == \$bigq1");
cmp_ok(is_eq(0.1, $bigq2), '!=', 1, "sv_numeq agrees that 0.1 != \$bigq2");
which resulted in:
ok 3 - sv_numeq agrees that 0.1 == $bigq1
not ok 4 - sv_numeq agrees that 0.1 != $bigq2
# Failed test 'sv_numeq agrees that 0.1 != $bigq2'
# at sv_numeq.pl line 25.
# got: 1
# expected: anything else
Oh ... and I think there's a simple typo in the docs:
<quote>
"sv_numeq"
A convenient shortcut for calling "sv_numeq" with the "SV_GMAGIC"
flag.
</quote>
I suspect the second occurrence of "sv_numeq" should be "sv_numeq_flags" ?
Cheers,
Rob
On Mon, Sep 13, 2021 at 1:59 AM Paul "LeoNerd" Evans <leonerd@leonerd.org.uk>
wrote:
> On Fri, 20 Aug 2021 11:55:00 +0100
> "Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:
>
> > I propose adding an API function
> >
> > BOOL sv_numeq(SV *left, SV *right);
> >
> > to cover this.
> >
>
> Update: here's an initial attempt, including docs and XS::APItests:
>
> https://github.com/leonerd/perl5/tree/sv_numeq
>
>
> Thinking further about this, I think I'd like to define a new SV_* flag
> to request that the function also perform operator overloading. This
> new sv_numeq() would recognise that flag, and I'd also add support to
> the existing sv_eq(). (Which, is stringy. In hindsight it should have
> been named sv_streq(), but too late now (unless we rename it maybe?))
>
>
> Perhaps it would be named something like `SV_TRYAMAGIC`. Or maybe the
> less cryptic `SV_OVERLOAD` (though we already have an unrelated flag,
> `SV_SKIP_OVERLOAD` which the various sv_2Xv_flags() functions all take).
>
> `SV_OVERLOAD` would be nice, except it's a bit messy having some
> _flags()-like functions that do overloading by default and have a flag
> to tell them not to, vs. other flagsy functions that don't but take a
> flag to tell them to.
>
>
> Unless.....
>
> Maybe I can solve all of these issues in one go, by making a new
> sv_streq_flags() which does overload by default, and put that behaviour
> in my new sv_numeq_flags() as well, then these functions can all respond
> to the same SV_SKIP_OVERLOAD flag in the same way. Then sv_eq() and
> sv_eq_flags() become wrappers of sv_streq_flags() which do pass the
> SV_SKIP_OVERLOAD flag. I think it would be nice to do overload by
> default as that's generally "the nice thing to do", and a flag to turn
> it off for specific circumstances.
>
>
> Thoughts?
>
> --
> Paul "LeoNerd" Evans
>
> leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
> http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
>