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

Commit 5edf944

Browse files
Reini Urbanrurban
Reini Urban
authored andcommitted
exact_arith: implement it
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.
1 parent e7fd7c3 commit 5edf944

18 files changed

+456
-51
lines changed

Configure

+28
Original file line numberDiff line numberDiff line change
@@ -1418,6 +1418,7 @@ CONFIG=''
14181418
usecperl=''
14191419
fake_signatures=''
14201420
usenamedanoncv=''
1421+
useexactarith=''
14211422

14221423
: Detect odd OSs
14231424
define='define'
@@ -7584,6 +7585,32 @@ $define)
75847585
;;
75857586
esac
75867587

7588+
case "$useexactarith" in
7589+
$define|true|[yY]*) dflt='y' ;;
7590+
*) dflt='n' ;;
7591+
esac
7592+
cat <<EOM
7593+
7594+
Would you like to build Perl so that every builtin arithmetic overflow
7595+
does not promote to internal fast double/long double numbers but
7596+
instead to slow and precise bigint? So that exact_arith is enabled by default.
7597+
7598+
If this doesn't make any sense to you, just accept the default '$dflt'.
7599+
EOM
7600+
rp='Use exact arith, overflow to bigint as in perl6?'
7601+
. ./myread
7602+
case "$ans" in
7603+
y|Y) val="$define" ;;
7604+
*) val="$undef" ;;
7605+
esac
7606+
set useexactarith
7607+
eval $setvar
7608+
case "$useexactarith" in
7609+
$define)
7610+
echo "exact_arith selected." >&4
7611+
;;
7612+
esac
7613+
75877614
case "$usecperl" in
75887615
$define) usecperl='define'
75897616
echo "cperl variant selected." >&4
@@ -25860,6 +25887,7 @@ usecrosscompile='$usecrosscompile'
2586025887
usedevel='$usedevel'
2586125888
usedl='$usedl'
2586225889
usedtrace='$usedtrace'
25890+
useexactarith='$useexactarith'
2586325891
usefaststdio='$usefaststdio'
2586425892
useffi='$useffi'
2586525893
useithreads='$useithreads'

MANIFEST

+2
Original file line numberDiff line numberDiff line change
@@ -5494,6 +5494,8 @@ lib/dumpvar.pl A variable dumper
54945494
lib/dumpvar.t A variable dumper tester
54955495
lib/English.pm Readable aliases for short variables
54965496
lib/English.t See if English works
5497+
lib/exact_arith.pm Pragma to set exact arithmetic as in perl6
5498+
lib/exact_arith.t See if use exact_arith works
54975499
lib/ExtUtils/Embed.pm Utilities for embedding Perl in C programs
54985500
lib/ExtUtils/t/Embed.t See if ExtUtils::Embed and embedding works
54995501
lib/ExtUtils/typemap Extension interface types

Porting/Maintainers.pl

+1
Original file line numberDiff line numberDiff line change
@@ -1735,6 +1735,7 @@ package Maintainers;
17351735
lib/DBM_Filter/
17361736
lib/DirHandle.{pm,t}
17371737
lib/English.{pm,t}
1738+
lib/exact_arith.{pm,t}
17381739
lib/ExtUtils/Embed.pm
17391740
lib/ExtUtils/XSSymSet.pm
17401741
lib/ExtUtils/t/Embed.t

config_h.SH

+8
Original file line numberDiff line numberDiff line change
@@ -5457,6 +5457,14 @@ sed <<!GROK!THIS! >$CONFIG_H -e 's!^#undef\(.*/\)\*!/\*#define\1 \*!' -e 's!^#un
54575457
#$usesafehashiter USE_SAFE_HASHITER /**/
54585458
#endif
54595459
5460+
/* USE_EXACT_ARITH:
5461+
* This symbol, if defined, indicates that Perl uses exact_arith as default.
5462+
*/
5463+
#define PERL_EXACT_ARITH
5464+
#ifndef USE_EXACT_ARITH
5465+
#$useexactarith USE_EXACT_ARITH /**/
5466+
#endif
5467+
54605468
/* PERL_HASH_FUNC_*:
54615469
* This symbol defines the used perl hash function variant.
54625470
* It is set in Configure or via -Dhash_func=, but can be left blank.

embed.fnc

+1
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,7 @@ pR |int |PerlSock_accept_cloexec|int listenfd \
501501
pR |int |PerlSock_socketpair_cloexec|int domain|int type|int protocol \
502502
|NN int *pairfd
503503
#endif
504+
AMp |void |bigint_arith |NN const char *op|NN SV *const left|NN SV *const right
504505
#if defined(PERL_IN_DOIO_C)
505506
s |IO * |openn_setup |NN GV *gv|NN char *mode|NN PerlIO **saveifp \
506507
|NN PerlIO **saveofp|NN int *savefd \

embed.h

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
#define av_top_index(a) S_av_top_index(aTHX_ a)
7373
#define av_undef(a) Perl_av_undef(aTHX_ a)
7474
#define av_unshift(a,b) Perl_av_unshift(aTHX_ a,b)
75+
#define bigint_arith(a,b,c) Perl_bigint_arith(aTHX_ a,b,c)
7576
#define block_end(a,b) Perl_block_end(aTHX_ a,b)
7677
#define block_gimme() Perl_block_gimme(aTHX)
7778
#define block_start(a) Perl_block_start(aTHX_ a)

ext/Config/Config_xs.in

+1
Original file line numberDiff line numberDiff line change
@@ -1489,6 +1489,7 @@ usecrosscompile, T_INV,0,ALN64I"@@usecrosscompile@@"
14891489
usedevel, T_INV,0,ALN64I"@@usedevel@@"
14901490
usedl, T_INV,0,ALN64I"@@usedl@@"
14911491
usedtrace, T_INV,0,ALN64I"@@usedtrace@@"
1492+
useexactarith, T_INV,0,ALN64I"@@useexactarith@@"
14921493
usefaststdio, T_INV,0,ALN64I"@@usefaststdio@@"
14931494
useffi, T_INV,0,ALN64I"@@useffi@@"
14941495
useithreads, T_INV,0,ALN64I"@@useithreads@@"

lib/exact_arith.pm

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package exact_arith;
2+
our $VERSION = '0.01';
3+
my $HINT_EXACT_ARITH = 0x0000010; # see perl.h
4+
5+
sub import {
6+
use Math::BigInt try => 'GMP';
7+
#$^H{exact_arith} = 1;
8+
$^H |= $HINT_EXACT_ARITH;
9+
}
10+
sub unimport {
11+
#delete $^H{exact_arith};
12+
$^H &= ~$HINT_EXACT_ARITH;
13+
}
14+
15+
1;
16+
__END__
17+
18+
=head1 NAME
19+
20+
exact_arith - promote on overflow to bigint/num
21+
22+
=head1 SYNOPSIS
23+
24+
use exact_arith;
25+
print 18446744073709551614 * 2; # => 36893488147419103228, a Math::BigInt object
26+
27+
{ no exact_arith;
28+
print 18446744073709551614 * 2; # => 3.68934881474191e+19
29+
}
30+
31+
=head1 DESCRIPTION
32+
33+
This is a new lexical user-pragma since cperl5.24 to use exact
34+
arithmetic, without loosing precision on all builtin arithmetic ops.
35+
As in perl6.
36+
37+
It is of course a bit slower than without, but it's much faster than
38+
perl6, since it only does use bigint on IV/UV overflows which do
39+
happen very seldom.
40+
41+
=cut

lib/exact_arith.t

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!./perl -- -*- mode: cperl; cperl-indent-level: 4 -*-
2+
3+
BEGIN {
4+
chdir 't' if -d 't';
5+
@INC = ( '.', '../lib' );
6+
}
7+
8+
use strict;
9+
require '../t/test.pl';
10+
plan(8);
11+
12+
$|=1;
13+
my $a = 18446744073709551614;
14+
15+
# test it at compile-time in constant folding
16+
use exact_arith;
17+
my $n = 18446744073709551614 * 2; # => 36893488147419103228, Math::BigInt or *::GMP
18+
like(ref $n, qr/^Math::BigInt/, '* type (c)');
19+
ok($n eq '36893488147419103228', '* val (c)') or
20+
is($n, '36893488147419103228');
21+
22+
{
23+
no exact_arith;
24+
my $m = 18446744073709551614 * 2;
25+
is(ref $m, '', '* no type (c)');
26+
is($m, 3.68934881474191e+19, '* no val (c)');
27+
}
28+
29+
my $two = 2;
30+
$n = 18446744073709551614 * $two; # run-time
31+
like(ref $n, qr/^Math::BigInt/, '* type (r)');
32+
ok($n eq '36893488147419103228', '* val (r)') or
33+
is($n, '36893488147419103228');
34+
35+
{
36+
no exact_arith;
37+
my $m = 18446744073709551614 * $two;
38+
is(ref $m, '', '* no type (r)');
39+
is($m, 3.68934881474191e+19, '* no val (r)');
40+
}
41+
42+
my $c = 18446744073709551614 + 10000;
43+
like(ref $c, qr/^Math::BigInt/, '+ type (c)');
44+
my $r = $a + 10000;
45+
like(ref $r, qr/^Math::BigInt/, '+ type (r)');
46+
47+
$c = 18446744073709551624 - 2;
48+
like(ref $c, qr/^Math::BigInt/, '- type (c)');
49+
$r = $c - 1;
50+
like(ref $r, qr/^Math::BigInt/, '- type (r)');
51+
52+
$c = 1844674407370955162400 / 0.3;
53+
like(ref $c, qr/^Math::BigInt/, '/ type (c)');
54+
$r = 1844674407370955162400 / 0.3;
55+
like(ref $r, qr/^Math::BigInt/, '/ type (r)');
56+
57+
$c = 18446744073709551614 ** 2;
58+
like(ref $c, qr/^Math::BigInt/, '** type (c)');
59+
$r = $a ** 2;
60+
like(ref $r, qr/^Math::BigInt/, '** type (r)');
61+
62+
$r = $a++;
63+
like(ref $r, qr/^Math::BigInt/, '++ type (r)');
64+

perl.c

+6
Original file line numberDiff line numberDiff line change
@@ -1899,6 +1899,9 @@ S_Internals_V(pTHX_ CV *cv)
18991899
# ifdef PERL_DONT_CREATE_GVSV
19001900
" PERL_DONT_CREATE_GVSV"
19011901
# endif
1902+
# ifdef PERL_EXACT_ARITH
1903+
" PERL_EXACT_ARITH"
1904+
# endif
19021905
# ifdef PERL_EXTERNAL_GLOB
19031906
" PERL_EXTERNAL_GLOB"
19041907
# endif
@@ -1998,6 +2001,9 @@ S_Internals_V(pTHX_ CV *cv)
19982001
# ifdef USE_CPERL
19992002
" USE_CPERL"
20002003
# endif
2004+
# ifdef USE_EXACT_ARITH
2005+
" USE_EXACT_ARITH"
2006+
# endif
20012007
# ifdef USE_FAST_STDIO
20022008
" USE_FAST_STDIO"
20032009
# endif

perl.h

+6-4
Original file line numberDiff line numberDiff line change
@@ -314,9 +314,12 @@
314314
RX_ENGINE(rx_sv)->dupe(aTHX_ (rx_sv),(param))
315315
#endif
316316

317-
318-
319-
317+
#ifdef PERL_EXACT_ARITH
318+
#define IS_EXACT_ARITH PL_curcop->cop_hints & HINT_EXACT_ARITH
319+
/*#define IS_EXACT_ARITH cop_hints_fetch_pvs(PL_curcop, "exact_arith", REFCOUNTED_HE_EXISTS)*/
320+
#else
321+
#define IS_EXACT_ARITH 0
322+
#endif
320323

321324
/*
322325
* Because of backward compatibility reasons the PERL_UNUSED_DECL
@@ -5418,7 +5421,6 @@ END_EXTERN_C
54185421
#define HINT_UTF8 0x00800000 /* utf8 pragma */
54195422

54205423
#define HINT_NO_AMAGIC 0x01000000 /* overloading pragma */
5421-
54225424
#define HINT_RE_FLAGS 0x02000000 /* re '/xism' pragma */
54235425

54245426
#define HINT_FEATURE_MASK 0x1c000000 /* 3 bits (4,8,10) for feature bundles */

pod/perlcdelta.pod

+4-2
Original file line numberDiff line numberDiff line change
@@ -305,9 +305,11 @@ release manager will have to investigate the situation carefully.)
305305

306306
=over 4
307307

308-
=item *
308+
=item L<exact_arith> 0.01
309309

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

312314
=back
313315

0 commit comments

Comments
 (0)