Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Re: Detecting whether UV fits into an NV

by dave_the_m (Monsignor)
on Feb 26, 2020 at 09:27 UTC ( [id://11113426]=note: print w/replies, xml ) Need Help??


in reply to Detecting whether UV fits into an NV

Why not just cast the UV value to an NV, cast back to an UV, and see if the first and final values differ?

Dave

  • Comment on Re: Detecting whether UV fits into an NV

Replies are listed 'Best First'.
Re^2: Detecting whether UV fits into an NV
by syphilis (Archbishop) on Feb 26, 2020 at 10:58 UTC
    Why not just cast the UV value to an NV, cast back to an UV, and see if the first and final values differ?

    I think that checking whether the UV arg == (UV)((NV)arg) is a splendid idea.
    Interestingly, it's only very slightly faster (at least for the value ranges I've tested) than the XSub I posted at the beginning of this thread, but it's far, far simpler.

    As an aside, whilst this approach is quite simple to implement inside XS space, is it even possible to do inside perl space ?
    That is, inside a perl sub, how would one coerce a UV to an NV and then back to a UV ?
    (This aspect is not an issue for me - which is the reason that I've presented it "as an aside". Just curious as to if/how it's possible, that's all.)

    Thanks Dave.

    Cheers,
    Rob
      In general perl goes to great trouble to use the type which will be as lossless as possible rather than the type you want, so it wouldn't be easy. Maybe messing about with pack/unpack?

      Dave.

      I think that checking whether the UV arg == (UV)((NV)arg) is a splendid idea

      Unfortunately this check fails for some UV values with less recent Microsoft Compilers - eg Visual Studio 2010 and earlier.
      No problems with Visual Studio 2017 and Visual Studio 2019.
      I suspect that Visual Studio 2015 is the oldest Microsoft Compiler that doesn't suffer from this problem, but I haven't confirmed that.

      This particular issue looks like another manifestation of the problem that sent me down this path in the first place.
      Here's the demo I ran:
      use strict; use warnings; use Inline C => Config => BUILD_NOISY => 1; use Inline C => <<'EOC'; int uv_fits_double1( UV arg ) { if(arg == (UV)((NV)arg)) return 1; return 0; } int uv_fits_double2(UV arg) { while(!(arg & 1)) arg >>= 1; if(arg < 9007199254740993) return 1; return 0; } EOC my $ls = 63; my @in = ( 18446744073709549568, 18446744073709139968); for(@in) { print "$_: ", uv_fits_double1($_),uv_fits_double2($_), "\n" +; } __END__ With Windows Server 2003 SP1 Platform SDK, this outputs: 18446744073709549568: 01 18446744073709139968: 01 Base 2 representations of the 2 Uvs is (resp): 1111111111111111111111111111111111111111111111111111100000000000 1111111111111111111111111111111111111111111110011011100000000000 showing that both are exactly representable by a double.


      Cheers,
      Rob
        Once you phrased it this way, I realized you finally gave me an in-the-wild use-case for my Data::IEEE754::Tools. Thanks! :-)

        #!/usr/bin/env perl use strict; use warnings; use Data::IEEE754::Tools qw/ulp/; sub uv_fits_double_using_ulp { 0 == ($_[0] % ($_[0]/ulp($_[0]))) } my @in = map { no warnings 'portable'; chomp; oct('0b'.$_) } <DATA>; for(@in) { my $d = $_/ulp($_); printf "%-24s: %4d %32.15f %16d %1s\n", $_, ulp($_), $_/ulp($_), $ +_ % $d, uv_fits_double_using_ulp($_) ? 'T' : 'F'; } __END__ 1111111111111111111111111111111111111111111111111111100000000000 1111111111111111111111111111111111111111111111111111110000000000 1111111111111111111111111111111111111111111111111111100000000001 1111111111111111111111111111111111111111111110011011100000000000 1111111111111111111111111111111111111111111111111111100000000000 0111111111111111111111111111111111111111111111111111110000000000 0011111111111111111111111111111111111111111111111111111000000000 0001111111111111111111111111111111111111111111111111111100000000 0000111111111111111111111111111111111111111111111111111110000000 0000011111111111111111111111111111111111111111111111111111000000 0000001111111111111111111111111111111111111111111111111111100000 0000000111111111111111111111111111111111111111111111111111110000 0000000011111111111111111111111111111111111111111111111111111000 0000000001111111111111111111111111111111111111111111111111111100 0000000000111111111111111111111111111111111111111111111111111110 0000000000011111111111111111111111111111111111111111111111111111 0000000000111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111
        which gives
        18446744073709549568 : 2048 9007199254740991.000000000000000 + 0 T 18446744073709550592 : 4096 4503599627370496.000000000000000 450359 +9627369472 F 18446744073709549569 : 2048 9007199254740991.000000000000000 + 1 F 18446744073709139968 : 2048 9007199254740791.000000000000000 + 0 T 18446744073709549568 : 2048 9007199254740991.000000000000000 + 0 T 9223372036854774784 : 1024 9007199254740991.000000000000000 + 0 T 4611686018427387392 : 512 9007199254740991.000000000000000 + 0 T 2305843009213693696 : 256 9007199254740991.000000000000000 + 0 T 1152921504606846848 : 128 9007199254740991.000000000000000 + 0 T 576460752303423424 : 64 9007199254740991.000000000000000 + 0 T 288230376151711712 : 32 9007199254740991.000000000000000 + 0 T 144115188075855856 : 16 9007199254740991.000000000000000 + 0 T 72057594037927928 : 8 9007199254740991.000000000000000 + 0 T 36028797018963964 : 4 9007199254740991.000000000000000 + 0 T 18014398509481982 : 2 9007199254740991.000000000000000 + 0 T 9007199254740991 : 1 9007199254740991.000000000000000 + 0 T 18014398509481983 : 4 4503599627370496.000000000000000 450359 +9627370495 F 18446744073709551615 : 4096 4503599627370496.000000000000000 450359 +9627370495 F

        I doubt it's faster than some of hte others

      my $x = ( 1 << 54 ) | 1; say $x; # 18014398509481985 $x = unpack 'F', pack 'F', $x; $x = unpack 'J', pack 'J', $x; say $x; # 18014398509481984

      To support negative numbers, use I instead of J for negative numbers.


      The following is a little bit truer to a cast, but it's far more complicated, far more fragile (can fail for overloaded and magical vars), and technically uses XS:

      use strict; use warnings; use feature qw( say ); use B qw( svref_2object ); my $x = ( 1 << 54 ) | 1; say $x; # 18014398509481985 # Add the value as an NV to the scalar. { no warnings qw( void ); log $x; } # Make it so the scalar contains just an NV. $x = svref_2object(\$x)->NV; # Add the value as an IV or UV to the scalar. { no warnings qw( void ); $x | 1; } # Make it so the scalar contains just an IV or UV. $x = svref_2object(\$x)->int_value; say $x; # 18014398509481984
        Cool - and thanks for going to the trouble of providing that. (I can spend hours trying to get pack and unpack to do what I want them to do ;-)
        Both of your approaches still fail to deal correctly with certain values when older Microsoft compilers are used.
        All is good for the value of 18014398509481985, irrespective of compiler. The value of $x changes from 18014398509481985 to 18014398509481984, indicating correctly that 18014398509481985 is not representable as a double.

        However, IIUC, the value of $x should not alter if $x is initialized to (eg) 18446744073709549568 or 18446744073709139968 as both values are representable as a double.
        But those values do change with those older Microsoft compilers - again, I believe, indicative of that same problem (that I keep hitting) that afflicted Microsoft compilers until some point after Visual Studio 2010 and no later than Visual Studio 2017.

        At this point, the only thing that's working on these troublesome Microsoft compilers are the subs I provided in my initial post.

        I should point out that I haven't yet got to assessing jcb's contribution. I'll try to get to it over the weekend, or whenever I can find the time.

        Cheers,
        Rob

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11113426]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others studying the Monastery: (7)
As of 2024-04-19 17:59 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found