Skip to content
This repository has been archived by the owner on Jun 1, 2023. It is now read-only.

Commit

Permalink
exact_arith: implement it
Browse files Browse the repository at this point in the history
Promote on overflow to bigint, and not to NV.
This is a new lexical user-pragma to use exact
arithmetic without loosing precision on all builtin arithmetic ops.
As in perl6, just much faster. (only in the rare 1% overflow case)

It is of course a bit slower than without, but I could not
measure any difference.

See #21.
  • Loading branch information
Reini Urban authored and rurban committed Jul 2, 2019
1 parent e099e77 commit e0fcf78
Show file tree
Hide file tree
Showing 18 changed files with 458 additions and 49 deletions.
28 changes: 28 additions & 0 deletions Configure
Expand Up @@ -1424,6 +1424,7 @@ CONFIG=''
usecperl=''
fake_signatures=''
usenamedanoncv=''
useexactarith=''

: Detect odd OSs
define='define'
Expand Down Expand Up @@ -7584,6 +7585,32 @@ $define)
;;
esac

case "$useexactarith" in
$define|true|[yY]*) dflt='y' ;;
*) dflt='n' ;;
esac
cat <<EOM

Would you like to build Perl so that every builtin arithmetic overflow
does not promote to internal fast double/long double numbers but
instead to slow and precise bigint? So that exact_arith is enabled by default.

If this doesn't make any sense to you, just accept the default '$dflt'.
EOM
rp='Use exact arith, overflow to bigint as in perl6?'
. ./myread
case "$ans" in
y|Y) val="$define" ;;
*) val="$undef" ;;
esac
set useexactarith
eval $setvar
case "$useexactarith" in
$define)
echo "exact_arith selected." >&4
;;
esac

case "$usecperl" in
$define) usecperl='define'
echo "cperl variant selected." >&4
Expand Down Expand Up @@ -25913,6 +25940,7 @@ usecrosscompile='$usecrosscompile'
usedevel='$usedevel'
usedl='$usedl'
usedtrace='$usedtrace'
useexactarith='$useexactarith'
usefaststdio='$usefaststdio'
useffi='$useffi'
useithreads='$useithreads'
Expand Down
2 changes: 2 additions & 0 deletions MANIFEST
Expand Up @@ -5686,6 +5686,8 @@ lib/dumpvar.pl A variable dumper
lib/dumpvar.t A variable dumper tester
lib/English.pm Readable aliases for short variables
lib/English.t See if English works
lib/exact_arith.pm Pragma to set exact arithmetic as in perl6
lib/exact_arith.t See if use exact_arith works
lib/ExtUtils/Embed.pm Utilities for embedding Perl in C programs
lib/ExtUtils/t/Embed.t See if ExtUtils::Embed and embedding works
lib/ExtUtils/typemap Extension interface types
Expand Down
1 change: 1 addition & 0 deletions Porting/Maintainers.pl
Expand Up @@ -1732,6 +1732,7 @@ package Maintainers;
lib/DBM_Filter/
lib/DirHandle.{pm,t}
lib/English.{pm,t}
lib/exact_arith.{pm,t}
lib/ExtUtils/Embed.pm
lib/ExtUtils/XSSymSet.pm
lib/ExtUtils/t/Embed.t
Expand Down
8 changes: 8 additions & 0 deletions config_h.SH
Expand Up @@ -5488,6 +5488,14 @@ sed <<!GROK!THIS! >$CONFIG_H -e 's!^#undef\(.*/\)\*!/\*#define\1 \*!' -e 's!^#un
#$usesafehashiter USE_SAFE_HASHITER /**/
#endif
/* USE_EXACT_ARITH:
* This symbol, if defined, indicates that Perl uses exact_arith as default.
*/
#define PERL_EXACT_ARITH
#ifndef USE_EXACT_ARITH
#$useexactarith USE_EXACT_ARITH /**/
#endif
/* PERL_HASH_FUNC_*:
* This symbol defines the used perl hash function variant.
* It is set in Configure or via -Dhash_func=, but can be left blank.
Expand Down
1 change: 1 addition & 0 deletions embed.fnc
Expand Up @@ -528,6 +528,7 @@ pR |int |PerlSock_accept_cloexec|int listenfd \
pR |int |PerlSock_socketpair_cloexec|int domain|int type|int protocol \
|NN int *pairfd
#endif
AMp |void |bigint_arith |NN const char *op|NN SV *const left|NN SV *const right
#if defined(PERL_IN_DOIO_C)
s |IO * |openn_setup |NN GV *gv|NN char *mode|NN PerlIO **saveifp \
|NN PerlIO **saveofp|NN int *savefd \
Expand Down
1 change: 1 addition & 0 deletions embed.h
Expand Up @@ -67,6 +67,7 @@
#define av_top_index(a) S_av_top_index(aTHX_ a)
#define av_undef(a) Perl_av_undef(aTHX_ a)
#define av_unshift(a,b) Perl_av_unshift(aTHX_ a,b)
#define bigint_arith(a,b,c) Perl_bigint_arith(aTHX_ a,b,c)
#define block_end(a,b) Perl_block_end(aTHX_ a,b)
#define block_gimme() Perl_block_gimme(aTHX)
#define block_start(a) Perl_block_start(aTHX_ a)
Expand Down
1 change: 1 addition & 0 deletions ext/Config/Config_xs.in
Expand Up @@ -1496,6 +1496,7 @@ usecrosscompile, T_INV,0,ALN64I"@@usecrosscompile@@"
usedevel, T_INV,0,ALN64I"@@usedevel@@"
usedl, T_INV,0,ALN64I"@@usedl@@"
usedtrace, T_INV,0,ALN64I"@@usedtrace@@"
useexactarith, T_INV,0,ALN64I"@@useexactarith@@"
usefaststdio, T_INV,0,ALN64I"@@usefaststdio@@"
useffi, T_INV,0,ALN64I"@@useffi@@"
useithreads, T_INV,0,ALN64I"@@useithreads@@"
Expand Down
41 changes: 41 additions & 0 deletions lib/exact_arith.pm
@@ -0,0 +1,41 @@
package exact_arith;
our $VERSION = '0.01';
my $HINT_EXACT_ARITH = 0x0000010; # see perl.h

sub import {
use Math::BigInt try => 'GMP';
#$^H{exact_arith} = 1;
$^H |= $HINT_EXACT_ARITH;
}
sub unimport {
#delete $^H{exact_arith};
$^H &= ~$HINT_EXACT_ARITH;
}

1;
__END__
=head1 NAME
exact_arith - promote on overflow to bigint/num
=head1 SYNOPSIS
use exact_arith;
print 18446744073709551614 * 2; # => 36893488147419103228, a Math::BigInt object
{ no exact_arith;
print 18446744073709551614 * 2; # => 3.68934881474191e+19
}
=head1 DESCRIPTION
This is a new lexical user-pragma since cperl 5.32 to use exact
arithmetic, without loosing precision on all builtin arithmetic ops.
As in perl6.
It is of course a bit slower than without, but it's much faster than
perl6, since it only does use bigint on IV/UV overflows which do
happen very seldom.
=cut
64 changes: 64 additions & 0 deletions lib/exact_arith.t
@@ -0,0 +1,64 @@
#!./perl -- -*- mode: cperl; cperl-indent-level: 4 -*-

BEGIN {
chdir 't' if -d 't';
@INC = ( '.', '../lib' );
}

use strict;
require '../t/test.pl';
plan(8);

$|=1;
my $a = 18446744073709551614;

# test it at compile-time in constant folding
use exact_arith;
my $n = 18446744073709551614 * 2; # => 36893488147419103228, Math::BigInt or *::GMP
like(ref $n, qr/^Math::BigInt/, '* type (c)');
ok($n eq '36893488147419103228', '* val (c)') or
is($n, '36893488147419103228');

{
no exact_arith;
my $m = 18446744073709551614 * 2;
is(ref $m, '', '* no type (c)');
is($m, 3.68934881474191e+19, '* no val (c)');
}

my $two = 2;
$n = 18446744073709551614 * $two; # run-time
like(ref $n, qr/^Math::BigInt/, '* type (r)');
ok($n eq '36893488147419103228', '* val (r)') or
is($n, '36893488147419103228');

{
no exact_arith;
my $m = 18446744073709551614 * $two;
is(ref $m, '', '* no type (r)');
is($m, 3.68934881474191e+19, '* no val (r)');
}

my $c = 18446744073709551614 + 10000;
like(ref $c, qr/^Math::BigInt/, '+ type (c)');
my $r = $a + 10000;
like(ref $r, qr/^Math::BigInt/, '+ type (r)');

$c = 18446744073709551624 - 2;
like(ref $c, qr/^Math::BigInt/, '- type (c)');
$r = $c - 1;
like(ref $r, qr/^Math::BigInt/, '- type (r)');

$c = 1844674407370955162400 / 0.3;
like(ref $c, qr/^Math::BigInt/, '/ type (c)');
$r = 1844674407370955162400 / 0.3;
like(ref $r, qr/^Math::BigInt/, '/ type (r)');

$c = 18446744073709551614 ** 2;
like(ref $c, qr/^Math::BigInt/, '** type (c)');
$r = $a ** 2;
like(ref $r, qr/^Math::BigInt/, '** type (r)');

$r = $a++;
like(ref $r, qr/^Math::BigInt/, '++ type (r)');

6 changes: 6 additions & 0 deletions perl.c
Expand Up @@ -1932,6 +1932,9 @@ S_Internals_V(pTHX_ CV *cv)
# ifdef PERL_DONT_CREATE_GVSV
" PERL_DONT_CREATE_GVSV"
# endif
# ifdef PERL_EXACT_ARITH
" PERL_EXACT_ARITH"
# endif
# ifdef PERL_EXTERNAL_GLOB
" PERL_EXTERNAL_GLOB"
# endif
Expand Down Expand Up @@ -2034,6 +2037,9 @@ S_Internals_V(pTHX_ CV *cv)
# ifdef USE_CPERL
" USE_CPERL"
# endif
# ifdef USE_EXACT_ARITH
" USE_EXACT_ARITH"
# endif
# ifdef USE_FAST_STDIO
" USE_FAST_STDIO"
# endif
Expand Down
10 changes: 6 additions & 4 deletions perl.h
Expand Up @@ -317,9 +317,12 @@
RX_ENGINE(rx_sv)->dupe(aTHX_ (rx_sv),(param))
#endif




#ifdef PERL_EXACT_ARITH
#define IS_EXACT_ARITH PL_curcop->cop_hints & HINT_EXACT_ARITH
/*#define IS_EXACT_ARITH cop_hints_fetch_pvs(PL_curcop, "exact_arith", REFCOUNTED_HE_EXISTS)*/
#else
#define IS_EXACT_ARITH 0
#endif

/*
* Because of backward compatibility reasons the PERL_UNUSED_DECL
Expand Down Expand Up @@ -5442,7 +5445,6 @@ END_EXTERN_C
#define HINT_UTF8 0x00800000 /* utf8 pragma */

#define HINT_NO_AMAGIC 0x01000000 /* overloading pragma */

#define HINT_RE_FLAGS 0x02000000 /* re '/xism' pragma */

#define HINT_FEATURE_MASK 0x1c000000 /* 3 bits (4,8,10) for feature bundles */
Expand Down
6 changes: 6 additions & 0 deletions pod/perlcdelta.pod
Expand Up @@ -555,6 +555,12 @@ release manager will have to investigate the situation carefully.)

=over 4

=item L<exact_arith> 0.01

Promote on overflow to bigint/bignum as in perl6, do not loose precision
with all builtin arithmetic operators.
L<[cperl #21]|https://github.com/perl11/cperl/issues/21>

=item L<ffi> 0.01c

ffi helpers and ffi types.
Expand Down

0 comments on commit e0fcf78

Please sign in to comment.