HaxeFoundation / haxe

Haxe - The Cross-Platform Toolkit
https://haxe.org
6.17k stars 654 forks source link

[doc] Modulo operator gives incorrect result on certain Floats #7070

Closed neillrobson closed 5 years ago

neillrobson commented 6 years ago

I am almost certain that I am simply misunderstanding some basic element of the modulo (%) operator's behavior in Haxe, but I can't seem to figure out where in the world my understanding is incorrect (or from where Haxe is pulling certain results).

At the very least, if the following behavior is expected, I believe that some form of documentation explaining the rationale would be helpful to newcomers such as myself.

Minimum code to reproduce:

// File: Main.hx
class Main
{
    static var rotation:Float = 47;

    static public function main():Void
    {
        trace('rotation: ' + rotation);
        trace('rotation + 45: ' + (rotation + 45));
        trace('(rotation + 45) / 90: ' + ((rotation + 45) / 90));
        trace('(rotation + 45) / 90 % 4: ' + ((rotation + 45) / 90 % 4));
        trace('Math.round((rotation + 45) / 90) % 4: ' + Math.round((rotation + 45) / 90) % 4);
        trace('Math.round((rotation + 45) / 90 % 4): ' + Math.round((rotation + 45) / 90 % 4));
    }
}

Running the code:

$ haxe -main Main --interp

Expected output:

Main.hx:7: rotation: 47
Main.hx:8: rotation + 45: 92
Main.hx:9: (rotation + 45) / 90: 1.0222222222222221
Main.hx:10: (rotation + 45) / 90 % 4: 1.0222222222222221
Main.hx:11: Math.round((rotation + 45) / 90) % 4: 1
Main.hx:12: Math.round((rotation + 45) / 90 % 4): 1

Actual output:

Main.hx:7: rotation: 47
Main.hx:8: rotation + 45: 92
Main.hx:9: (rotation + 45) / 90: 1.0222222222222221
Main.hx:10: (rotation + 45) / 90 % 4: 46   // Where is 46 coming from?
Main.hx:11: Math.round((rotation + 45) / 90) % 4: 1
Main.hx:12: Math.round((rotation + 45) / 90 % 4): 46   // Where is 46 coming from?
Simn commented 6 years ago

In Haxe, % has higher precedence than /. Don't ask me why though...

neillrobson commented 6 years ago

Oh. Thank you @Simn . I can't believe that I missed such an obvious issue of precedence. My apologies for any noise I may have added to the repository with this ticket, and thank you for your patience.

Simn commented 6 years ago

Let's keep this open, we should document our operator precedence better. Plus I'm curious why we treat % with higher precedence, given that neither C nor JS do that.

@ncannasse

ncannasse commented 6 years ago

This is current manual page: https://haxe.org/manual/types-numeric-operators.html

This is the previous one I wrote some time ago, that lists the differences: http://old.haxe.org/manual/operators

Simn commented 6 years ago

Yes but the question was why it is different for %.

ncannasse commented 6 years ago

The fact that C/C++ has "ambiguous" operators (different class of operators with the same precedence) is the source of many bugs, and compilers often now warns about such cases.

In Haxe our operators groups (with same precedence) are only within the same mathematical context [*/] [+-] [bit shifts] [bit ops] [opassigns]

So % should be in different group than / and * . That it has higher precedence is a matter of taste. In the following expressions:

a%b/c a/c%b

I feel these are more likely interpreted as:

(a%b)/c a/(c%b)

Simn commented 6 years ago

This is one of these cases where I don't think your feelings justify deviating from the standard, but at the same time it's not worth breaking anything at this point. So yeah, let's document this.

neillrobson commented 6 years ago

Yeah, I think that the decision is perfectly fine as long as it is documented. I've used my fair share of programming languages with eccentric operator precedence. They are often implemented with good reasoning, but that reasoning is rarely well-documented. Including such documentation for Haxe would put it a step above the rest!

RblSb commented 5 years ago

Doc fixed! https://haxe.org/manual/types-numeric-operators.html

Simn commented 5 years ago

Right, thanks for the reminder!