We don't bite newbies here... much PerlMonks

### [OT: JavaScript] JS remainder operation ('%')

by syphilis (Archbishop)
 on Jan 17, 2024 at 03:47 UTC Need Help??

syphilis has asked for the wisdom of the Perl Monks concerning the following question:

Hi,

11155911 aroused my curiosity about JS arithmetic, and I've lately been working on emulating that arithmetic in perl. (By "working, I mean "playing" ;-)
One thing that's puzzling me is the JS calculation of the expression 900719925474099.7 % 2147483647.83.
According to 2 online JS calculators (https://playcode.io/new and https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Remainder) the result is 859064762.882.
But my (double precision) calculation results in the slightly different 859064762.875:
```>perl -le "\$x = 900719925474099.7 - (2147483647.83 * 419430); print \$x
+;"
859064762.875
(The value of 419430 is arrived at by evaluating int(900719925474099.7 / 2147483647.83).)
The online JS calculators agree that 900719925474099.7 - (2147483647.83 * 419430) results in 859064762.875, so apparently their % operation is doing something slightly different.
What am I overlooking ?

The "JS remainder documentation" that I'm looking at is also the second of the 2 links given above.
As regards calculation of r = n % d, it contains:

<quote>
When both operands are non-zero and finite, the remainder r is calculated as r := n - d * q where q is the integer such that r has the same sign as the dividend n while being as close to 0 as possible.
</quote>

Have I misunderstood that documentation ? (I don't know what the := symbol signifies ... I assume it's meant to be taken as the assignment operator.)
Any enlightenment appreciated.

Cheers,
Rob

PS - this is part of a not-particularly-useful-work-in-progress at https://github.com/sisyphus/math-js

Replies are listed 'Best First'.
Re: [OT: JavaScript] JS remainder operation ('%')
by ikegami (Patriarch) on Jan 17, 2024 at 17:45 UTC

According to 2 online JS calculators (https://playcode.io/new and https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Remainder) the result is 859064762.882.

I get 859064762.875 from both of those when using FireFox, and from the FireFox console. (It's not surprising they all give the same result since they all use the JS engine in my browser.)

I get 859064762.875 from both of those when using Chrome, and from the Chrome console. (Again, the consistency is to be expected.)

What kind of machine are you using?

Note that they are are two common modulo operators in CS ("floored" and "truncated"), and it varies a lot by language.

 Perl % Floored POSIX::fmod Truncated JavaScript % Truncated

This only matters if an operands is negative, though.

Keep in mind that there is a IEEE rounding setting that on x86 usually defaults to "round toward even", but could also be set to round down or round up. This could be relevant since the implementation of fmod involves a float-to-int conversion.
Copying this into the address bar
• javascript:document.writeln(900719925474099.7 % 2147483647.83)
is producing 859064762.882 on my android's browsers

• Chrome
• Duckduckgo
• Opera
I suppose you are on windows?

##### update

it's also producing 859064762.882 on my Ubuntu18's

• Chromium
• Firefox

Cheers Rolf
(addicted to the Perl Programming Language :)
see Wikisyntax for the Monastery

What kind of machine are you using?

What kind are you using ?
Apparently yours uses the sub FMOD { return \$_[0] - int(\$_[0] / \$_[1]) * \$_[1]; } that harangzsolt33 posted, not the fmod() implementation that most(?) of us are seeing.

Cheers,
Rob

Nevermind, I just noticed that you changed the code before running it in Perl.

Re: [OT: JavaScript] JS remainder operation ('%')
by soonix (Canon) on Jan 17, 2024 at 09:55 UTC
Up to now it was my understanding that the "remainder" operation is defined for integer operands only. With Floats, you have to deal with rounding errors.
Up to now it was my understanding that the "remainder" operation is defined for integer operands only.

That sounds sane to me - but why would these online calculators then provide a value for the remainder when one or both inputs is non-integer ?
Surely, the sane thing to do would be to throw an exception.

With Floats, you have to deal with rounding errors

That's not really a problem if you're prepared to apply (and adhere to) consistent rounding practice.
Sure, that practice doesn't avoid rounding, but it does mean that there's only one correct result.
Also, for the particular example I've given, the difference between the 2 results (0x1.99a24dd70e56p+29 versus 0x1.99a24dd7p+29) appears to be more than a rounding discrepancy.
(To my eyes, it looks more like a precision discrepancy.)

Sorry- it might seem that I'm "taking you to task" over your response, and that's not my intention.
I'm quite happy to accept that it's silly to be thinking about this, but I just wanted to present those counter-arguments in case they have some validity.

Cheers,
Rob
Strictly speaking is Modulo only defined for positive integers, excluding 0 for the dividend.

Many languages take incompatible liberties in extending this definition by relying on the same implementation/algorithm, but also allowing floats as input.

The rounding errors theory in your case is very convincing. But you should also check results with negative values and error cases

> Surely, the sane thing to do would be to throw an exception.

NB: JS is particularly reluctant to throw exceptions, it rather follows the "quiet" NaN (= not a number) approach, to silently fail.

IMHO it's impossible to translate this behavior to efficient pure Perl.

Cheers Rolf
(addicted to the Perl Programming Language :)
see Wikisyntax for the Monastery

##### Update

I have problems imagining a use case for float modulo operations, except reimplementing division.

Any ideas?

Re: [OT: JavaScript] JS remainder operation ('%')
by syphilis (Archbishop) on Jan 19, 2024 at 23:37 UTC
Thanks for all the replies.
Math-JS-0.03 is now on CPAN, for anyone interested.
(Duh ... I've already noticed one typo in the POD ...)

Cheers,
Rob
I've already noticed one typo in the POD

The best time for spotting typos is immediately after hitting 'GO'...

Perhaps that's because we all need a few finctions in our lives 😜

So true. IIRC one of the major email players (probably a webmail one but am hazy on the details) created an option to hold a message for 30 seconds or so after the user pressed "Send" for just such occurrences. Seemed like a really good idea for us fat-fingered hippos.

Update: It was gmail.

🦛

Re: [OT: JavaScript] JS remainder operation ('%')
by Anonymous Monk on Jan 17, 2024 at 12:51 UTC

Also: The spec, Java at TIO

```https://t.ly/EHF3Z
https://t.ly/m7AYI
```
https://t.ly/EHF3Z

Thank You !!!
From that link: this may be compared with the C library function fmod.
They're just using the fmod() function from the standard C math.h:
```## try.pl ##
use strict;
use warnings;

use Inline C => <<'EOC';

double foo(double x, double y) {
return fmod(x, y);
}

EOC

print foo(900719925474099.7, 2147483647.83);

__END__

Outputs: 859064762.882
How is it possible that the XP of Anonymous Monk is less than that of Vroom ???
(Defies all logic ;-)

Cheers,
Rob
Alternatively
```#! /usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

use POSIX qw{ fmod };

say fmod(900719925474099.7, 2147483647.83);

map{substr\$_->[0],\$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: [OT: JavaScript] JS remainder operation ('%')
by Bod (Parson) on Jan 19, 2024 at 22:21 UTC
The online JS calculators agree...

I've just built a quick and dirty JS calculator to test the result. My browser, Chrome on Windows 10, agrees with the online calculators you have tried...

If you suspect it is a browser/OS variance then other Monks with different configurations might like to visit:
https://www.boddison.com/temp/PMJStest.html

Re: [OT: JavaScript] JS remainder operation ('%')
by harangzsolt33 (Chaplain) on Jan 17, 2024 at 17:31 UTC
JavaScript's % operator is different. The Perl equivalent would be this :

```sub FMOD { return \$_[0] - int(\$_[0] / \$_[1]) * \$_[1]; }

Perl's % operator behaves the same as the MOD operator in QBASIC.

sub FMOD { return \$_[0] - int(\$_[0] / \$_[1]) * \$_[1]; }

That's exactly what I was using - and, for the given example, it returns 859064762.875, whereas the JS implementations return 859064762.882.
In using C's fmod() function, although the input arguments and the output result are standard 53-bit doubles, the calculations are being done to a higher precision (presumably for improved accuracy).
Here's a demo script:
```use strict;
use warnings;
use Math::MPFR qw(:mpfr);

print      FMOD(900719925474099.7, 2147483647.83), "\n";
print MPFR_FMOD(900719925474099.7, 2147483647.83), "\n";

sub FMOD { return \$_[0] - int(\$_[0] / \$_[1]) * \$_[1]; }

sub MPFR_FMOD {
# Do exactly what FMOD does - but on Math::MPFR objects, performing
# the calculation at 200-bit precision.

my \$arg0 = Math::MPFR->new(\$_[0]); # 53 bit precision representatio
+n of \$_[0]
my \$arg1 = Math::MPFR->new(\$_[1]); # 53 bit precision representatio
+n of \$_[1]

# Do the calculation at 200 bit precision for improved accuracy.
Rmpfr_set_default_prec(200);

my \$ret = int(\$arg0 / \$arg1);
\$ret *= \$arg1;
\$ret = \$arg0 - \$ret;

# Convert the 200-bit precision \$ret to
# a perl scalar (double), and return it
my \$nv = Rmpfr_get_d(\$ret, MPFR_RNDN);

# Restore default precision back to the
# original value of 53 before returning
Rmpfr_set_default_prec(53);

return \$nv;
}

__END__

Outputs:
859064762.875
859064762.882
I just need to use C's fmod function, or any other equivalent thereof.

Cheers,
Rob
Okay. Let me simplify the problem:

Try this in JavaScript:

var A = 900719066409336.9;
alert(A); // This will display: 900719066409336.9

Try this in Perl:

my \$A = 900719066409336.9;
print \$A; # This will print: 900719066409337

This is QBASIC 1.1 code:

LET A# = 900719066409336.9#
PRINT A# ' This will print: 900719066409336.9

So, there's the problem. As you can see, at one point, the FMOD() function that we have divides the numbers that you picked in your example, and then it takes the integer part of that. Everything works okay so far. We get the same result in all three languages. But then we multiply this number by 2147483647.83, and that's when things go bad.

In JavaScript and even in ancient QBASIC 1.1, we get the correct result. But in Perl, the double variable loses precision for some reason.

It gets more interesting, because if you use printf instead of print in Perl, you further get different results:

my \$A = 900719066409336.9;

print \$A; # This will print: 900719066409337

printf('%.1f', \$A); # This will print 900719066409336.9

printf('%.2f', \$A); # This will print 900719066409336.87

printf('%.3f', \$A); # This will print 900719066409336.870

Man, this is weird! I have no idea what's going on.

Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11157037]
Approved by GrandFather
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (7)
As of 2024-05-21 09:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?

No recent polls found