in reply to Trying to translate overflowing JS code to Perl
Based on Corion's unpack( "l", pack "L", ($value & 0xffffffff))) I have put together a package which overloads basic arithmetic operators in an attempt to do these operations with 32bit integers thus simulating JS's 32bit integers. It succeeds in 1169367104<<5 = -1234958336 but fails for 1073741824+1073741824 (that's 1<<30 + 1<<30) (Perl result overflows: -2147483648 but it does not in JS: 2147483648). I have also tried NERDVANA's method of converting to 32bit in C and also with doing the addition above in C. These fail as well for said test case (1073741824+1073741824)
5 mins edit - sorry: Well 1073741824 = 1000000000000000000000000000000 = 1<<30 (that's 31 digits leaving the 32nd for sign), so 1073741824+1073741824=1000000000000000000000000000000+1000000000000000000000000000000 should overflow into the sign bit. Producing negative zero (so to speak). But Perl with 32bit integers (and Corion's hack) produces -2147483648 (ok perhaps i am missing the details on what happens with the value-part in overflowing situations) and (node.js) JS produces 2147483648 as if no overflow occured. Perhaps I have got JS's default integer size wrong? Nope! results are for node.js!!! when I enter 1<<30 + 1<<30 into firefox JS console, I get 0. Which is the negative zero. Why Perl produces -2147483648. Which takes the single bit which is ON to be both value and sign?
2hour Edit: I have changed the package code slightly to use Corion's method by default.
Here is Math::AnyInt:
package Math::AnyInt; #use 5.006; use v5.36; use strict; use warnings; our $VERSION = '0.01'; use Exporter qw(import); our @EXPORT = qw( calcop VERBOSE_AnyInt ); # caller can set this to 0 or 1 our $VERBOSE_AnyInt = 0; # overload basic arithmetic operators use overload '+' => sub { # we get 3 params: 1+2 are objects of this class, swap is ... well # get the value of the objects, do the operation # and then convert to 32bit my ($self, $other, $swap) = @_; if( $VERBOSE_AnyInt > 0 ){ print STDOUT "+ : operating on ".(defin +ed($self)&&defined($$self)?$$self:'<undef>')." and ".(defined($other) +?$other:'<undef>')." (swap is ".(defined($swap)?($swap?'yes':'no'):'< +undef>').")\n" } Math::AnyInt->new( calc_32bit_signed($self->value() + $other->valu +e()) ); }, '-' => sub { my ($self, $other, $swap) = @_; if( $VERBOSE_AnyInt > 0 ){ print STDOUT "- : operating on ".(defin +ed($self)&&defined($$self)?$$self:'<undef>')." and ".(defined($other) +?$other:'<undef>')." (swap is ".(defined($swap)?($swap?'yes':'no'):'< +undef>').")\n" } return $swap ? Math::AnyInt->new( calc_32bit_signed($self->value() - $other-> +value()) ) : Math::AnyInt->new( calc_32bit_signed($other->value() - $self-> +value()) ) ; }, '*' => sub { my ($self, $other, $swap) = @_; if( $VERBOSE_AnyInt > 0 ){ print STDOUT "* : operating on ".(defin +ed($self)&&defined($$self)?$$self:'<undef>')." and ".(defined($other) +?$other:'<undef>')." (swap is ".(defined($swap)?($swap?'yes':'no'):'< +undef>').")\n" } Math::AnyInt->new( calc_32bit_signed($self->value() + $other->valu +e()) ); }, '/' => sub { my ($self, $other, $swap) = @_; if( $VERBOSE_AnyInt > 0 ){ print STDOUT "/ : operating on ".(defin +ed($self)&&defined($$self)?$$self:'<undef>')." and ".(defined($other) +?$other:'<undef>')." (swap is ".(defined($swap)?($swap?'yes':'no'):'< +undef>').")\n" } return $swap ? Math::AnyInt->new( calc_32bit_signed($self->value() / $other-> +value()) ) : Math::AnyInt->new( calc_32bit_signed($other->value() / $self-> +value()) ) ; }, '<<' => sub { my ($self, $other, $swap) = @_; if( $VERBOSE_AnyInt > 0 ){ print STDOUT "<< : operating on ".(defi +ned($self)&&defined($$self)?$$self:'<undef>')." and ".(defined($other +)?$other:'<undef>')." (swap is ".(defined($swap)?($swap?'yes':'no'):' +<undef>').")\n" } # if operands were swapped, it does matter in this case if( $swap ){ die "<< : order is important why is swap on?" } Math::AnyInt->new( calc_32bit_signed($self->value() << $other->val +ue()) ); }, '>>' => sub { my ($self, $other, $swap) = @_; if( $VERBOSE_AnyInt > 0 ){ print STDOUT ">> : operating on ".(defi +ned($self)&&defined($$self)?$$self:'<undef>')." and ".(defined($other +)?$other:'<undef>')." (swap is ".(defined($swap)?($swap?'yes':'no'):' +<undef>').")\n" } if( $swap ){ die "<< : order is important why is swap on?" } Math::AnyInt->new( calc_32bit_signed($self->value() >> $other->val +ue()) ); }, '**' => sub { my ($self, $other, $swap) = @_; if( $VERBOSE_AnyInt > 0 ){ print STDOUT "** : operating on ".(defi +ned($self)&&defined($$self)?$$self:'<undef>')." and ".(defined($other +)?$other:'<undef>')." (swap is ".(defined($swap)?($swap?'yes':'no'):' +<undef>').")\n" } if( $swap ){ die "<< : order is important why is swap on?" } Math::AnyInt->new( calc_32bit_signed($self->value() ** $other->val +ue()) ); }, '""' => sub { ${$_[0]} } ; sub new { my ($class, $val) = @_; return bless \$val } # Corion's converting to 32bit signed int sub calc_32bit_signed { return unpack("l", pack("L", $_[0])) } ##sub calc_32bit_signed { unpack( "l", pack "L", ($_[0] & 0xffffffff)) + } # NERDVANA's converting to 32bit signed int #sub calc_32bit_signed { return calc_32bit_signed_in_C($_[0]) } use Inline 'C', <<~'END'; #include <stdint.h> long calc_32bit_signed_in_C(long input) { return (int32_t)((int32_t)input); } long test_addition_in_C(long l, long r) { return (int32_t)((int32_t)l + (int32_t)r); } END # return the integer value contain in this object # which is just a ref to the value (see new()) sub value { return ${ $_[0] } } # this does the arithmetic when the operator is in string format, usef +ul for testing sub calcop { my ($lop, $op, $rop) = @_; if( ref($lop) ne __PACKAGE__ ){ die 'calcop()'." : left-operant mu +st be a ".__PACKAGE__." object but it is just '".ref($lop)."'."; } if( ref($rop) ne __PACKAGE__ ){ die 'calcop()'." : right-operant m +ust be a ".__PACKAGE__." object but it is just '".ref($rop)."'."; } if( $VERBOSE_AnyInt > 0 ){ print STDOUT 'calcop()'." : called for +'$$lop' '$op' '$$rop' ...\n" } if( $op eq '+' ){ return $lop + $rop; } elsif( $op eq '-' ){ return $lop - $rop } elsif( $op eq '*' ){ return $lop * $rop } elsif( $op eq '/' ){ return $lop / $rop } elsif( $op eq '<<' ){ return $lop << $rop } elsif( $op eq '>>' ){ return $lop >> $rop } elsif( $op eq '**' ){ return $lop ** $rop } die 'calcop()'." : unknown operator '$op'."; } 1;
And here is a basic test harness:
#!/usr/bin/env perl use strict; use warnings; use lib 'blib/lib'; our $VERSION = '0.01'; use Test::More; use Math::AnyInt; $Math::AnyInt::VERBOSE_AnyInt = 1; my @tests = ( # left-operand, operator, right-operand, expected result in JS [1, '+', 2, 3], [1169367104, '<<', 5, -1234958336], [1073741824, '+', 1073741824, 2147483648], ); for my $at (@tests){ my ($lop, $op, $rop, $expected) = @$at; my $LOP = Math::AnyInt->new($lop); is($$LOP, $lop, 'Math::AnyInt->new()'." : called and contains valu +e '$lop'."); my $ROP = Math::AnyInt->new($rop); is($$ROP, $rop, 'Math::AnyInt->new()'." : called and contains valu +e '$rop'."); my $result = Math::AnyInt::calcop($LOP, $op, $ROP); is($$result, $expected, "$lop $op $rop : got $result, expected $ex +pected."); } # test addition of two integers by taking it in C via Inline::C # and using C's int32_t (NERDVANA) is(Math::AnyInt::test_addition_in_C(1073741824, 1073741824), 214748364 +8, "Testing NERDVANA's C additon calc()"); # END done_testing();
bw, bliako
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re^2: Trying to translate overflowing JS code to Perl
by syphilis (Archbishop) on Dec 01, 2023 at 08:10 UTC | |
by bliako (Abbot) on Dec 01, 2023 at 10:35 UTC | |
by syphilis (Archbishop) on Dec 01, 2023 at 11:31 UTC | |
by bliako (Abbot) on Dec 01, 2023 at 12:48 UTC | |
by syphilis (Archbishop) on Dec 02, 2023 at 01:52 UTC |