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

Commit 8b8af00

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 64fb820 commit 8b8af00

18 files changed

+456
-56
lines changed

Configure

+28
Original file line numberDiff line numberDiff line change
@@ -1424,6 +1424,7 @@ CONFIG=''
14241424
usecperl=''
14251425
fake_signatures=''
14261426
usenamedanoncv=''
1427+
useexactarith=''
14271428

14281429
: Detect odd OSs
14291430
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
@@ -25913,6 +25940,7 @@ usecrosscompile='$usecrosscompile'
2591325940
usedevel='$usedevel'
2591425941
usedl='$usedl'
2591525942
usedtrace='$usedtrace'
25943+
useexactarith='$useexactarith'
2591625944
usefaststdio='$usefaststdio'
2591725945
useffi='$useffi'
2591825946
useithreads='$useithreads'

MANIFEST

+2
Original file line numberDiff line numberDiff line change
@@ -5686,6 +5686,8 @@ lib/dumpvar.pl A variable dumper
56865686
lib/dumpvar.t A variable dumper tester
56875687
lib/English.pm Readable aliases for short variables
56885688
lib/English.t See if English works
5689+
lib/exact_arith.pm Pragma to set exact arithmetic as in perl6
5690+
lib/exact_arith.t See if use exact_arith works
56895691
lib/ExtUtils/Embed.pm Utilities for embedding Perl in C programs
56905692
lib/ExtUtils/t/Embed.t See if ExtUtils::Embed and embedding works
56915693
lib/ExtUtils/typemap Extension interface types

Porting/Maintainers.pl

+1
Original file line numberDiff line numberDiff line change
@@ -1732,6 +1732,7 @@ package Maintainers;
17321732
lib/DBM_Filter/
17331733
lib/DirHandle.{pm,t}
17341734
lib/English.{pm,t}
1735+
lib/exact_arith.{pm,t}
17351736
lib/ExtUtils/Embed.pm
17361737
lib/ExtUtils/XSSymSet.pm
17371738
lib/ExtUtils/t/Embed.t

config_h.SH

+8
Original file line numberDiff line numberDiff line change
@@ -5488,6 +5488,14 @@ sed <<!GROK!THIS! >$CONFIG_H -e 's!^#undef\(.*/\)\*!/\*#define\1 \*!' -e 's!^#un
54885488
#$usesafehashiter USE_SAFE_HASHITER /**/
54895489
#endif
54905490
5491+
/* USE_EXACT_ARITH:
5492+
* This symbol, if defined, indicates that Perl uses exact_arith as default.
5493+
*/
5494+
#define PERL_EXACT_ARITH
5495+
#ifndef USE_EXACT_ARITH
5496+
#$useexactarith USE_EXACT_ARITH /**/
5497+
#endif
5498+
54915499
/* PERL_HASH_FUNC_*:
54925500
* This symbol defines the used perl hash function variant.
54935501
* 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
@@ -528,6 +528,7 @@ pR |int |PerlSock_accept_cloexec|int listenfd \
528528
pR |int |PerlSock_socketpair_cloexec|int domain|int type|int protocol \
529529
|NN int *pairfd
530530
#endif
531+
AMp |void |bigint_arith |NN const char *op|NN SV *const left|NN SV *const right
531532
#if defined(PERL_IN_DOIO_C)
532533
s |IO * |openn_setup |NN GV *gv|NN char *mode|NN PerlIO **saveifp \
533534
|NN PerlIO **saveofp|NN int *savefd \

embed.h

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
#define av_top_index(a) S_av_top_index(aTHX_ a)
6868
#define av_undef(a) Perl_av_undef(aTHX_ a)
6969
#define av_unshift(a,b) Perl_av_unshift(aTHX_ a,b)
70+
#define bigint_arith(a,b,c) Perl_bigint_arith(aTHX_ a,b,c)
7071
#define block_end(a,b) Perl_block_end(aTHX_ a,b)
7172
#define block_gimme() Perl_block_gimme(aTHX)
7273
#define block_start(a) Perl_block_start(aTHX_ a)

ext/Config/Config_xs.in

+1
Original file line numberDiff line numberDiff line change
@@ -1496,6 +1496,7 @@ usecrosscompile, T_INV,0,ALN64I"@@usecrosscompile@@"
14961496
usedevel, T_INV,0,ALN64I"@@usedevel@@"
14971497
usedl, T_INV,0,ALN64I"@@usedl@@"
14981498
usedtrace, T_INV,0,ALN64I"@@usedtrace@@"
1499+
useexactarith, T_INV,0,ALN64I"@@useexactarith@@"
14991500
usefaststdio, T_INV,0,ALN64I"@@usefaststdio@@"
15001501
useffi, T_INV,0,ALN64I"@@useffi@@"
15011502
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
@@ -1932,6 +1932,9 @@ S_Internals_V(pTHX_ CV *cv)
19321932
# ifdef PERL_DONT_CREATE_GVSV
19331933
" PERL_DONT_CREATE_GVSV"
19341934
# endif
1935+
# ifdef PERL_EXACT_ARITH
1936+
" PERL_EXACT_ARITH"
1937+
# endif
19351938
# ifdef PERL_EXTERNAL_GLOB
19361939
" PERL_EXTERNAL_GLOB"
19371940
# endif
@@ -2031,6 +2034,9 @@ S_Internals_V(pTHX_ CV *cv)
20312034
# ifdef USE_CPERL
20322035
" USE_CPERL"
20332036
# endif
2037+
# ifdef USE_EXACT_ARITH
2038+
" USE_EXACT_ARITH"
2039+
# endif
20342040
# ifdef USE_FAST_STDIO
20352041
" USE_FAST_STDIO"
20362042
# 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
@@ -5439,7 +5442,6 @@ END_EXTERN_C
54395442
#define HINT_UTF8 0x00800000 /* utf8 pragma */
54405443

54415444
#define HINT_NO_AMAGIC 0x01000000 /* overloading pragma */
5442-
54435445
#define HINT_RE_FLAGS 0x02000000 /* re '/xism' pragma */
54445446

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

pod/perlcdelta.pod

+4-7
Original file line numberDiff line numberDiff line change
@@ -521,14 +521,11 @@ release manager will have to investigate the situation carefully.)
521521

522522
=over 4
523523

524-
=item L<ffi> 0.01c
524+
=item L<exact_arith> 0.01
525525

526-
ffi helpers and ffi types.
527-
528-
=item L<hashiter>
529-
530-
Allow hash iterators changing keys for back-compat.
531-
See L</Protected hash iterators>.
526+
Promote on overflow to bigint/bignum as in perl6, do not loose precision
527+
with all builtin arithmetic operators.
528+
L<[cperl #21]|https://github.com/perl11/cperl/issues/21>
532529

533530
=item L<YAML::Safe> 0.80
534531

0 commit comments

Comments
 (0)