Code Monkey home page Code Monkey logo

Comments (8)

Crissov avatar Crissov commented on July 20, 2024 2

I’ve frequently had the need for a non-zero modulo function, where zmod(a*b, b) returns b instead of 0, e.g. when calculating ordinal indices starting with 1 as in dates. This is easily done with mod(a*b - 1, b) + 1, except I’m not sure this always behaves as expected for parameters being negative or zero.

PS: Apparently, Mathematica has a generalized modulo function with variable offset d for this: Mod[a, n, d].

Math.mod = function ( dividend, divisor, offset = 0 ) {
    var remainder = (dividend - offset) % divisor + offset;
    if ( remainder * divisor < 0 ) {
        remainder += divisor;/*?*/
    }
    return remainder;
};

(Naively adapted from the code @Andrew-Cottrell had posted.)

from proposal-math-extensions.

ljharb avatar ljharb commented on July 20, 2024 2

can't you maybeZero || b in that case?

from proposal-math-extensions.

Andrew-Cottrell avatar Andrew-Cottrell commented on July 20, 2024 1

The following implementation, of the suggested function, appears to satisfy the criteria described above.

/**
 * A modulo operation that finds the remainder after division of a dividend
 * by divisor, using Donald E. Knuth's floored division.
 * 
 * The remainder will have an absolute value less than the absolute
 * value of the divisor. Unlike the ECMAScript remainder (%)
 * operator, the remainder will have the same sign as the divisor.
 * 
 * @param {number|BigInt} dividend - A finite number.
 * @param {number|BigInt} divisor - A non-zero number.
 * @return {number|BigInt} The remainder.
 * @throws {TypeError} If exactly one of the parameters is a BigInt.
 * @throws {RangeError} If the divisor is 0n.
 */
Math.mod = function ( dividend, divisor ) {
    /* based on https://github.com/google/closure-library/blob/v20210106/closure/goog/math/math.js#L68 */
    var remainder = dividend % divisor;
    if ( remainder * divisor < 0 ) {
        remainder += divisor;
    }
    return remainder;
};

/* if either dividend or divisor is not a number, return NaN */
Math.mod( "a", 1 );        // NaN
Math.mod( 1, "a" );        // NaN
Math.mod( NaN, 1 );        // NaN
Math.mod( 1, NaN );        // NaN

/* if divisor is a zero, return NaN or throw RangeError */
Math.mod( 1, 0 );          // NaN
Math.mod( 1, -0 );         // NaN
Math.mod( 1n, 0n );        // RangeError: Division by zero

/* if dividend is a zero, return the dividend */
Math.mod( 0, 1 );          // 0
Math.mod( -0, 1 );         // -0
Math.mod( 0, Infinity );   // 0
Math.mod( 0, -Infinity );  // 0
Math.mod( -0, Infinity );  // -0
Math.mod( -0, -Infinity ); // -0
Math.mod( 0n, 1n );        // 0n

/* if the dividend is an infinity, return NaN */
Math.mod( Infinity, 1 );   // NaN
Math.mod( -Infinity, 1 );  // NaN

/* if the divisor is an infinity with the same sign as the dividend, return the dividend */
Math.mod( 1, Infinity );   // 1
Math.mod( -1, -Infinity ); // -1

/* if the divisor is an infinity with the opposite sign as the dividend, return the divisor */
Math.mod( -1, Infinity );  // Infinity
Math.mod( 1, -Infinity );  // -Infinity

/* integers */
Math.mod( 4, 3 );          // 1
Math.mod( 4n, 3n );        // 1n
Math.mod( 4, -3 );         // -2
Math.mod( 4n, -3n );       // -2n
Math.mod( -4, 3 );         // 2
Math.mod( -4n, 3n );       // 2n
Math.mod( -4, -3 );        // -1
Math.mod( -4n, -3n );      // -1n

/* non-integers */
Math.mod( 5.5, 2 );        // 1.5
Math.mod( 5.5, -2 );       // -0.5
Math.mod( -5.5, 2 );       // 0.5
Math.mod( -5.5, -2 );      // -1.5

Except corner cases, this is the same as

from proposal-math-extensions.

Rudxain avatar Rudxain commented on July 20, 2024 1

My concern is that Math.mod may be an ambiguous name, and there's multiple valid definitions of mod, and there's even another definition not mentioned there which makes use of roundInf expand (rounding away from zero, and towards unsigned Infinity).

Even if none of those definitions are ever added (specially modCeil which is useless compared to modFloor), we should be aware of them, because maybe someday modEuclid gets added.

And we have another problem (only if more than 2 definitions are added): Should all be merged into 1 mod method with a 3rd string argument to specify the variant? Or should they all be splitted into standalone methods?

Yet another problem is modPow: should modPow only use 1 variant? Should it have a 4th arg to specify the variant? Or should it be split in multiple methods?

from proposal-math-extensions.

Andrew-Cottrell avatar Andrew-Cottrell commented on July 20, 2024 1

My concern is that Math.mod may be an ambiguous name...

In my own library, I named it floorMod (following Java's java.lang.Math.floorMod) to avoid ambiguity by clarifying the rounding mode. However, given Microsoft Excel and LibreOffice Calc use MOD, it is reasonable to use mod to mean the floored modulo. Something like modEuclid could be added, concurrently or later, with a longer name for — what I assume would be — less common use cases.

from proposal-math-extensions.

Rudxain avatar Rudxain commented on July 20, 2024 1

I agree, seems reasonable considering the "most" standard mod definition (in mathematics) is defined using floor division

from proposal-math-extensions.

 avatar commented on July 20, 2024 1

I'd like to add a modulo function, different from standard %. Let's call it Math.mod (x, d).

* if either x or d is not a number, return NaN.;

...

Note that these functions would just coerce their arguments to numbers instead.

from proposal-math-extensions.

Rudxain avatar Rudxain commented on July 20, 2024

Today I learned that what I call "roundInf" is actually named expand in the official and standard Intl.NumberFormat API. Here (at the roundingMode option). In Java, that same rounding mode is named UP.

I'll open a new Issue about rounding modes.
Update: #28

from proposal-math-extensions.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.