Perl / perl5

🐪 The Perl programming language
https://dev.perl.org/perl5/
Other
1.94k stars 554 forks source link

sqrt() gives incorrect answer for some big numbers #21864

Open rsFalse opened 9 months ago

rsFalse commented 9 months ago

Description

Somehow sqrt() truncates number 14142135.0000000353553... to integer value 14142135.

Steps to Reproduce

#!/usr/bin/perl -wl

use strict;

$_ = 199999982358225 ;
print join " ", map sqrt, $_, $_ + 1 ;
use bigint;
print join " ", sqrt 199999982358225, sqrt 199999982358226 ;
print join " ", map sqrt, $_, $_ + 1 ;
no bigint;
use bignum;
print join " ", map sqrt, $_, $_ + 1 ;
no bignum;
use bigint;
print join " ", sqrt 199999982358225, sqrt 199999982358226 ;

OUTPUT:

perl-5.38.2, perl-5.36.0
==========
Subroutine main::inf redefined at sqrt_2e14.pl line 11.
Subroutine main::NaN redefined at sqrt_2e14.pl line 11.
Subroutine main::inf redefined at sqrt_2e14.pl line 14.
Subroutine main::NaN redefined at sqrt_2e14.pl line 14.
14142135 14142135
14142135 14142135
14142135 14142135.00000003535534061865477701916612
14142135 14142135.00000003535534061865477701916612
14142135 14142135.00000003535534061865477701916612

perl-5.32.0, perl-5.20.1, perl-5.14.3
==========
14142135 14142135
14142135 14142135
14142135 14142135.00000003535534061865477701916612
14142135 14142135.00000003535534061865477701916612
14142135 14142135.00000003535534061865477701916612

Somehow warnings disappear when I use use warnings instead of -w flag.

I found several more similar size numbers, all of them are bigger than 1e14, each of them is square which has two non-square neighbors (+1 and -1):

111366421074841+-1
111366442180900+-1
129458792976016+-1
137919563015056+-1
149999982512704+-1
150000007007601+-1

The smallest which I found: 102609606529600+-1, which is sqr( 10129640 ), but sqr( 10129640 - 1 )+-1 have decimal point.

Expected behavior

Number 199999982358226, which is approx. 2e14, has no integer root.

Perl configuration

Summary of my perl5 (revision 5 version 38 subversion 2) configuration:

  Platform:
    osname=linux
    osvers=5.4.0-169-generic
    archname=x86_64-linux
...
    hint=recommended
    useposix=true
    d_sigaction=define
    useithreads=undef
    usemultiplicity=undef
    use64bitint=define
    use64bitall=define
    uselongdouble=undef
    usemymalloc=n
    default_inc_excludes_dot=define
  Compiler:
    cc='cc'
    ccflags ='-fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'
    optimize='-O2'
    cppflags='-fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include'
    ccversion=''
    gccversion='9.4.0'
    gccosandvers=''
    intsize=4
    longsize=8
    ptrsize=8
    doublesize=8
    byteorder=12345678
    doublekind=3
    d_longlong=define
    longlongsize=8
    d_longdbl=define
    longdblsize=16
    longdblkind=3
    ivtype='long'
    ivsize=8
    nvtype='double'
    nvsize=8
    Off_t='off_t'
    lseeksize=8
    alignbytes=8
    prototype=define
  Linker and Libraries:
    ld='cc'
    ldflags =' -fstack-protector-strong -L/usr/local/lib'
    libpth=/usr/local/lib /usr/lib/x86_64-linux-gnu /usr/lib /lib/x86_64-linux-gnu /lib /lib64 /usr/lib64
    libs=-lpthread -ldl -lm -lcrypt -lutil -lc
    perllibs=-lpthread -ldl -lm -lcrypt -lutil -lc
    libc=libc-2.31.so
    so=so
    useshrplib=false
    libperl=libperl.a
    gnulibc_version='2.31'
  Dynamic Linking:
    dlsrc=dl_dlopen.xs
    dlext=so
    d_dlsymun=undef
    ccdlflags='-Wl,-E'
    cccdlflags='-fPIC'
    lddlflags='-shared -O2 -L/usr/local/lib -fstack-protector-strong'

Characteristics of this binary (from libperl): 
  Compile-time options:
    HAS_LONG_DOUBLE
    HAS_STRTOLD
    HAS_TIMES
    PERLIO_LAYERS
    PERL_COPY_ON_WRITE
    PERL_DONT_CREATE_GVSV
    PERL_HASH_FUNC_SIPHASH13
    PERL_HASH_USE_SBOX32
    PERL_MALLOC_WRAP
    PERL_OP_PARENT
    PERL_PRESERVE_IVUV
    PERL_USE_SAFE_PUTENV
    USE_64_BIT_ALL
    USE_64_BIT_INT
    USE_LARGE_FILES
    USE_LOCALE
    USE_LOCALE_COLLATE
    USE_LOCALE_CTYPE
    USE_LOCALE_NUMERIC
    USE_LOCALE_TIME
    USE_PERLIO
    USE_PERL_ATOF
  Built under linux
  Compiled at Dec 22 2023 16:05:13
...
sisyphus commented 9 months ago
>perl -wle "print join ' ', map sqrt, 199999982358225, 199999982358226;"
14142135 14142135

Perl's print() function rounds the given values to 15 significant decimal digits - and at that precision the 2 square roots are the same, But the 2 values are different, as perl well knows:

>perl -wle "printf '%.17g %.17g', sqrt 199999982358225, sqrt 199999982358226;"
14142135 14142135.000000035

This unfortunate behaviour of the print() function has been around for a long time, and will probably remain for a few more years yet.

The warnings you see arise just because you've loaded both bigint and bignum:

>perl -Mbigint -Mbignum -wle "print 'ok'";
Subroutine main::inf redefined at -e line 0.
Subroutine main::NaN redefined at -e line 0.
ok

>perl -Mbignum -Mbigint -wle "print 'ok'";
Subroutine main::inf redefined at -e line 0.
Subroutine main::NaN redefined at -e line 0.
ok

I don't know why the warnings don't appear under the warnings pragma. Perhaps it's because the warnings pragma recognizes that these redefinitions are not really an issue. You could take this up with the bignum/bigint maintainer on CPAN if you want to pursue it.

D:\pscrpt>perl -Mbigint -Mbignum -le "no bignum; use bigint; print join ' ', sqrt 199999982358225, sqrt 199999982358226;"
14142135 14142135.00000003535534061865477701916612

D:\pscrpt>perl -Mbigint -le "print join ' ', sqrt 199999982358225, sqrt 199999982358226;"
14142135 14142135

I expected both of those one-liners to output 14142135 14142135. I don't know why the first one didn't - perhaps that's a bug in my thinking or perhaps a bug in bignum/bigint. Something else to take up with the bignum/bigint maintainer if you' want to pursue it.

However, I don't see anything here for the perl porters to consider, I think this issue should be closed unless, of course, I've missed something important

pjacklam commented 9 months ago

@Sisyphus wrote:

The warnings you see arise just because you've loaded both bigint and bignum:

>perl -Mbigint -Mbignum -wle "print 'ok'";
Subroutine main::inf redefined at -e line 0.
Subroutine main::NaN redefined at -e line 0.
ok

>perl -Mbignum -Mbigint -wle "print 'ok'";
Subroutine main::inf redefined at -e line 0.
Subroutine main::NaN redefined at -e line 0.

Yes, and both bigint and bignum export inf and NaN by default. The following issues no warnings:

perl -wle "use bigint(); use bignum();"

The following case puzzles me, though

$ perl -le "use bignum; no bignum; use bigint; print join ' ', sqrt 199999982358225, sqrt 199999982358226;"
14142135 14142135.00000003535534061865477701916612

but even bignum version 0.03 from 2002 (long before I got involved) gives this output, so at least it is nothing new. I'll look into this.

Grinnz commented 9 months ago

Somehow warnings disappear when I use use warnings instead of -w flag.

See https://perldoc.perl.org/warnings#What's-wrong-with-w-and-$%5EW - the -w flag is discouraged as it enables warnings in scopes that are not expecting them, leading to spurious warnings.