goe
goe

Reputation: 1243

Why does modulo operation gives different results in Java VS Perl?

I run this in Java 7 and I get:

double remainder1 = 1 % 1000;
double remainder2 = 0.01 % 1000;
double remainder3 = -1 % 1000;

System.out.println("START: "+remainder1+" | "+remainder2+" | "+remainder3);
>>>START: 1.0 | 0.01 | -1.0

But when I run the same operations in Perl 5.8.8 I get different results for two out of three:

my $remainder1 = 1 % 1000;
my $remainder2 = 0.01 % 1000;
my $remainder3 = -1 % 1000;
print "START: $remainder1 | $remainder2 | $remainder3";
>>>START: 1 | 0 | 999

Why is there such a difference in the last two calculations? How can I get perl to match java results?

Upvotes: 8

Views: 575

Answers (3)

mob
mob

Reputation: 118605

The second case:

  • % operates on integers and floating-point numbers in Java,
  • % only operates on integers in Perl.

The third case:

  • Java defines the modulus operation such that the following equation is true:

    dividend == ((int)(dividend/divisor)) * divisor + (dividend % divisor)
    
    e.g. -1 = 0 * 1000 + -1
    
  • Perl defines the modulus operation such that the following equation is true:

    $dividend == floor($dividend/$divisor) * divisor + ($dividend % $divisor)
    
    e.g. -1 = -1 * 1000 + 999
    

The Perl way has the advantage that the quotient (floor($dividend/$divisor)) will always have the same sign as the dividend.


To get the same behaviour as Java in Perl, use the POSIX::fmod function.

This is identical to the C function fmod().

$r = fmod($x, $y);

It returns the remainder $r = $x - $n*$y, where $n = trunc($x/$y). The $r has the same sign as $x and magnitude (absolute value) less than the magnitude of $y.

use POSIX 'fmod';

$m1 = fmod(1, 1000);     # 1
$m2 = fmod(0.01, 1000);  # 0.01
$m3 = fmod(-1, 1000);    # -1

Upvotes: 11

Saravana
Saravana

Reputation: 12817

Implemented the same in perl

sub modulo($$) {
        our $num1   = shift;
        our $num2   = shift;
        our $flip   = ( $num1 < 0 ? -1 : 1 ) * ( $num2 < 0 ? -1 : 1 );
        our $power1 = length substr $num1, index $num1, ".";
        our $power2 = length substr $num2, index $num2, ".";
        our $power  = $power1 > $power2 ? $power1 : $power2;
        ( ( abs($num1) * ( 10 ** $power ) ) % ( abs($num2) * ( 10 ** $power ) ) ) * $flip / ( 10 ** $power );
    }

print modulo( 1,    1000 ); # 1
print modulo( 0.01, 1000 ); # 0.01
print modulo( -1,   1000 ); # -1

Upvotes: 1

Bathsheba
Bathsheba

Reputation: 234715

The Java modulo operator can work with floating point types and also has the property that the result will be negative if the first argument is negative.

The Perl modulo operator is for integer types only and the result is always positive.

If you want the Perl one to behave like the Java one, then scale the fractions up to integers with an appropriate multiplication, take the modulus, and then divide to get the final result. Compensate for the negative sign manually. Although you might be better off building your own version of the operator yourself.

Upvotes: 6

Related Questions