Re: [OT: JavaScript] JS remainder operation ('%')
by soonix (Chancellor) 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. | [reply] |
|
|
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.
Thanks for the reply.
Cheers, Rob
| [reply] [d/l] [select] |
|
|
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.
Update
I have problems imagining a use case for float modulo operations, except reimplementing division.
Any ideas?
| [reply] |
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.
| [reply] |
|
|
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.
| [reply] |
|
|
| [reply] [d/l] [select] |
|
|
What kind of machine are you using?
Windows 11, with Google Chrome.
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
| [reply] [d/l] |
|
|
| [reply] |
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 | [reply] |
|
|
| [reply] |
|
|
| [reply] |
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
| [reply] |
|
|
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 | [reply] [d/l] [select] |
|
|
#! /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]
| [reply] [d/l] [select] |
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
| [reply] |
Re: [OT: JavaScript] JS remainder operation ('%')
by harangzsolt33 (Deacon) 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. | [reply] [d/l] |
|
|
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 | [reply] [d/l] [select] |
|
|
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.
| [reply] |
|
|
|
|
|
|
|
|
|
|
|
|
|