arturo-lang / arturo

Simple, expressive & portable programming language for efficient scripting
http://arturo-lang.io
MIT License
696 stars 32 forks source link

[Comparison] add built-in function for "approximately equal" #1376

Open github-actions[bot] opened 8 months ago

github-actions[bot] commented 8 months ago

[Comparison] add built-in function for "approximately equal" This could serve in cases where we want to compare between weirdly-rounded floating-point numbers and integers, e.g.: 3.0000001 and 3. But: we'll obviously have to somehow "define" this... approximate equality.

https://github.com/arturo-lang/arturo/blob/411617a1906063cf0adfd3ac06804dc4b29403a0/src/library/Comparison.nim#L28


#=======================================
# Definitions
#=======================================

# TODO(Comparison) add built-in function for "approximately equal"
#  This could serve in cases where we want to compare between weirdly-rounded floating-point numbers and integers, e.g.: 3.0000001 and 3.
#  But: we'll obviously have to somehow "define" this... approximate equality.
#  labels: library, enhancement, open discussion

proc defineLibrary*() =

    #----------------------------
    # Functions
    #----------------------------

    # TODO(Comparison\compare) verify it's working right
    #  The main problem seems to be this vague `else:`.
    #  In a few words: Even comparisons that are simply not possible will return 1 (!)
    #  see also: https://github.com/arturo-lang/arturo/pull/1139#issuecomment-1509404906
    #  labels: library, critical, bug
    builtin "compare",
        alias       = unaliased, 
        op          = opNop,
        rule        = PrefixPrecedence,
        description = "compare given values and return -1, 0, or 1 based on the result",
        args        = {
            "valueA": {Any},
            "valueB": {Any}
        },
        attrs       = NoAttrs,
        returns     = {Integer},
        example     = """
            compare 1 2           ; => -1
            compare 3 3           ; => 0
            compare 4 3           ; => 1
        """:
            #=======================================================
            if x < y:
                push(I1M)
            elif x == y:
                push(I0)
            else:
                push(I1)

    #----------------------------
    # Predicates
    #----------------------------

    builtin "between?",
        alias       = thickarrowboth, 

d186efdc4b8a4aaae2d57591aa3c5fb5ee985a5e

RickBarretto commented 8 months ago

I think this roughly would be delimited by the user, being the default parameter 0.1.

The algorithm would be something like:

proc roughly(x, y: Numeric, amplitude: float = 0.2): bool =
  abs(x - y) <= (amplitude + 0.005)
; Working code:
roughly 1.5 1.7 ; |0.2| <= 0.2005
; => true
roughly 1.7 1.5 ; |0.2| <= 0.2005
; => true
roughly.ampl: 0.1 1.7 1.5 ; |0.2| <= 0.1005
; => false
Ryton commented 8 months ago

Wouldnt it make more sense to make roughly an attribute of equal then? Defaulting to 0, so no change to equal, (equal 1.0 1.2 == equal.roughly:0 1.0 1.2); equals true but equal.roughly: 0.2 1.0 1.1 would equal false

RickBarretto commented 8 months ago

@Ryton I loved your approach. If @drkameleon agree with that, I'm totally in!

BNAndras commented 8 months ago

equal.within: 0.2 1.0 1.1 indicates 0.2 is the acceptable deviance between the two numbers. Racket uses check-within in its testing library.

drkameleon commented 8 months ago

@Ryton I loved your approach. If @drkameleon agree with that, I'm totally in!

My only asterisk would be how common each of the uses is. I mean: I guess = (equal?) is far more common than its approximate use. So, having an attribute there, means we'd have to check this all the time, even for normal comparisons...

drkameleon commented 8 months ago

Re-thinking about it...

Counter-proposal combining elements of all suggestions that - I think - could work best:

BNAndras commented 8 months ago

Pyret is another language I've been working with. It uses within(tolerance) which returns a function we can give two numbers and get true if they're within the tolerance. They've defined expr1 is-roughly expr2 which is a shorthand for using within(0.000001) on both expressions. So 0.000001 might be an useful default value .

drkameleon commented 8 months ago

Pyret is another language I've been working with. It uses within(tolerance) which returns a function we can give two numbers and get true if they're within the tolerance. They've defined expr1 is-roughly expr2 which is a shorthand for using within(0.000001) on both expressions. So 0.000001 might be an useful default value .

Very interesting! Noted! 😉

stale[bot] commented 2 weeks ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.