8

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?

goe
  • 1,153
  • 2
  • 12
  • 24
  • Possible duplicate of [How does java do modulus calculations with negative numbers?](http://stackoverflow.com/questions/4403542/how-does-java-do-modulus-calculations-with-negative-numbers) – Eli Sadoff Nov 03 '16 at 14:29
  • 1
    Why do you expect that two arbitrary programming languages that for sure have different semantics and underlying programming models ... give you the same results? What exactly made you think that a perl $something acts exactly the same as a Java double?! Dont get me wrong, but that is like asking "hey, why do bananas taste different than apples?" (you see, they are both fruits, aren't they?!) – GhostCat Nov 03 '16 at 14:38
  • 1
    In sympathy to the OP, `%` is a similar operator to `*` and `/`, and the latter two work in the same way. – Bathsheba Nov 03 '16 at 14:39

3 Answers3

11

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
ikegami
  • 367,544
  • 15
  • 269
  • 518
mob
  • 117,087
  • 18
  • 149
  • 283
6

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.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • Behaviour for negative numbers is also different. But really, who uses `%` for negative or non-integral numbers? – Thilo Nov 03 '16 at 14:31
  • @Thilo: Just realised I only 1/2 answered it. Note that even the clever C++ bods only fully defined the negative argument case from C++11 onwards. Prior to that it was up to the implementation. – Bathsheba Nov 03 '16 at 14:32
  • 1
    @Thilo People who think `$float % 1` is a good way to get the fractional part of a number. – chepner Nov 03 '16 at 14:32
1

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
Saravana
  • 12,647
  • 2
  • 39
  • 57