19
\$\begingroup\$

In C++, there exists a a <=> b three-way comparison operator that, for numerical types, does the following:

  • If a < b, then return a negative number.
  • If a = b, then return a (mathematically speaking) sign-less number.
  • If a > b, then return a positive number.

The Challenge

Implement the shortest full program, function, method, or custom operator such that, when given two numbers of the same type a and b,

  • If a < b, then return -1.
  • If a = b, then return 0.
  • If a > b, then return 1.

Notes

  • If you're writing in x86 assembly, this question may be of use to you.
  • This is , so shortest answer in bytes wins!
\$\endgroup\$
10
  • 1
    \$\begingroup\$ Do we have to handle all combinations of inputs? (I.e. can we assume that a - b will yield the arithmetic result, even when a is the lowest representable value and b is the highest?) \$\endgroup\$ Mar 20 at 13:15
  • 2
    \$\begingroup\$ @TobySpeight You will need to handle all combinations of inputs. \$\endgroup\$
    – bigyihsuan
    Mar 20 at 13:22
  • 5
    \$\begingroup\$ Note that the C++ 3-way comparison is actually "compares equal to 0" and "compares less than 0" and "compares greater than 0". 1 and -1 are explicitly not required, by design, unlike your ask. I only mention this because your first paragraphs are not true. \$\endgroup\$
    – Yakk
    Mar 21 at 14:57
  • 1
    \$\begingroup\$ If a language has support for multiple numeric types, such as integers and floating point numbers, does it need to support all of them? Basically every solution using subtraction and supporting floating point numbers doesn't work. \$\endgroup\$
    – Tyilo
    Mar 22 at 9:47
  • 2
    \$\begingroup\$ @Tyilo the original draft of this challenge in sandbox had a bonus for supporting multiple numeric types, but was cut because "bonuses are bad". Pick whichever numeric type leads to the shortest solution. \$\endgroup\$
    – bigyihsuan
    Mar 22 at 13:04

56 Answers 56

1
2
2
\$\begingroup\$

AArch64 machine code, 16 bytes

AArch64's conditional select instructions get their time to shine.

0000000000000000 <spaceship>:
   0:   eb01001f    cmp x0, x1
   4:   9a9f07e0    cset x0, ne
   8:   da80a400    cneg x0, x0, lt
   c:   d65f03c0    ret

cset sets the destination register to 1 or 0 according to whether the condition code is true or false. (Fun fact: it's actually an alias for csinc x0, xzr, xzr, eq, where csinc X, Y, Z, cond sets X to Y if the condition is true, and to Z+1 if false.)

cneg X, Y, cond sets X either to -Y or Y according to whether the condition is true or false. (Alias for csneg X, Y, Y, cond.)

Several other permutations are also possible, such as

cset x0, gt              // x0 = (a > b) ? 1 : 0
csinv x0, x0, xzr, lt    // x0 = (a < b) ? x0 : ~0
\$\endgroup\$
2
\$\begingroup\$

Haskell, 24 23 bytes

a#b|a<b= -1|a>b=1|0<1=0

Try it online!

-1, thanks to Unrelated String!

The 'nicer' solution of ((pred.fromEnum).).compare is 26 bytes.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ True -> 0<1 for -1 \$\endgroup\$ Mar 23 at 1:40
2
\$\begingroup\$

Lua, 45 bytes

load"a,b=...return a>b and 1or a<b and-1or 0"

Try it online!

Lua has a strict type system, and there is no shorter way than a and 1or 0 to convert a boolean to an integer.

This version works for all comparable types (e.g. integer, floating point, and string).

Lua, 35 bytes

load"a,b=...return-1+2/(0^(a-b)+1)"

Try it online!

This is based on my answer to Output the sign. It gives the correct result for all floating point inputs, and most integer inputs.

It doesn't meet the requirements of this challenge for integer inputs, as it will fail when passed, for example, math.maxinteger and math.mininteger (as demonstrated in the test harness), because subtracting one from the other results in an overflow. But it does fully work for all floating point inputs, thus meeting the challenge's requirements for that type.

\$\endgroup\$
3
  • \$\begingroup\$ You can save six bytes by taking in program arguments and printing out the answer instead of using the load function like this \$\endgroup\$
    – bluswimmer
    Apr 10 at 11:37
  • \$\begingroup\$ @bluswimmer Thanks, but I chose a function because it's a more elegant form of presentation, takes its input in an actual type instead of as strings, and can easily process more than one input in the test harness. I don't know if it's even possible to make a TIO or ATO test harness in Lua that demonstrates more than one input with a full program. So you could consider my choice of language is "Lua function", which means that a full program wouldn't beat it, since it wouldn't be in the same language. \$\endgroup\$
    – Deadcode
    Apr 10 at 16:43
  • \$\begingroup\$ @bluswimmer Case in point: The TIO you gave me is comparing the input as strings, not numbers. Without yet having tried it yet, I'm guessing a full program to compare numbers will actually be longer due to the need to convert the strings to numbers. \$\endgroup\$
    – Deadcode
    Apr 10 at 16:49
2
\$\begingroup\$

Wolfram Language (Mathematica), 5 bytes

Order

Try it online!

Input Order[b, a]. Works on any atomic numbers.

Note the reversed order of inputs.

\$\endgroup\$
2
\$\begingroup\$

PowerShell, 19 bytes

[math]::Sign($a-$b)

Try it online!

\$\endgroup\$
2
\$\begingroup\$

minigolf, 18 bytes

TiiT*+1+,:1+s,T+__

Attempt This Online! (Since minigolf is not properly added to ATO yet, I'm using a Python runner here.)

Explanation

We use -1 as the base of our output. If a-b >= 0, we increment -1 a-b+1 times. However, if our output reaches 2, we decrement it back to 1.

T                 0, Base our signum output on -1 _
iiT*+             0, a + b * -1 _
1+                0, increment the result _
,                 0, Repeat that many times: _
  :1+s            0, Increment signum but keep the original value _
                  0, This is useful because we only want to _
                  0, decrement once in the 2 case _
  ,               0, If our previous signum value is 1: do this once _
    T+            0, Add signum value by -1 _
  _               0, End foreach loop _
_                 0, End foreach loop _

0, Implicit output the signum value left on the stack _
```
\$\endgroup\$
1
  • \$\begingroup\$ Welcome to Code Golf, and nice answer/language! \$\endgroup\$ Apr 15 at 3:13
1
\$\begingroup\$

Jelly, 2 bytes

_Ṡ

A dyadic Link that accepts \$a\$ on the left and \$b\$ on the right and yields \$a \lt = \gt b\$.

Try it online!

How?

_Ṡ - Link: a, b
_  - (a) subtract (b)
 Ṡ - sign -> -1, 0, or 1
\$\endgroup\$
1
\$\begingroup\$

ARBLE, 25 15 bytes

lt(a,b)-gt(a,b)

Same as Arnauld's JS solution. Unfortunately Verbose, throwing away the advantages of the langauge.

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Japt, 2 bytes

gV

Try it

g   # sign of the difference of implicit first input and
 V  # second input
\$\endgroup\$
2
  • \$\begingroup\$ What does V do, then? \$\endgroup\$
    – Seggan
    Mar 20 at 1:55
  • 1
    \$\begingroup\$ @Seggan This program transpiles to the JS U.g(V), where U is the first input and V is the second \$\endgroup\$
    – noodle man
    Mar 20 at 1:56
1
\$\begingroup\$

Thunno, \$ 3\log_{256}(96)\approx\$ 2.47 bytes

_za

Attempt This Online!

Sign of a-b.

\$\endgroup\$
1
\$\begingroup\$

Brachylog, 2 bytes

-ṡ

Try it online!

Same answer as many other esolangs.

Explanation

-     Subtract
 ṡ    Sign
\$\endgroup\$
1
\$\begingroup\$

Ruby, 12 bytes

->a,b{a<=>b}

Try it online!

(feels like cheating)

\$\endgroup\$
5
  • 2
    \$\begingroup\$ I suggest adding this to the CW for languages with the exact <=> operator. \$\endgroup\$ Mar 20 at 15:53
  • 2
    \$\begingroup\$ ->{_1<=>_2} is one byte shorter. \$\endgroup\$
    – south
    Mar 21 at 1:17
  • \$\begingroup\$ @south ->{_1<=>_2} doesn't seem to work when copy-pasted into TIO... \$\endgroup\$ Mar 21 at 9:05
  • \$\begingroup\$ TIO uses an out-of-date ruby version. Implicit block parameters were added after 2.5.5. \$\endgroup\$
    – south
    Mar 21 at 20:35
  • 2
    \$\begingroup\$ proc &:<=> is 10 bytes. \$\endgroup\$
    – naffetS
    Mar 22 at 3:54
1
\$\begingroup\$

simply, 8 bytes

Does exactly the expected.

&compare

This is a built-in function that simply returns the expected result.


Example:

Code-like

// Should output 1
echo &compare(2, 1);

Plain-English-like

// Should output -1
Show the result of calling &compare with the arguments 1, 2.

Test code:

This shows test results in a nice pretty table.

// Test data - self-explanatory
// Example 5 shows an error, as an example
$tests = &json_decode(<<<JSON
[
    {"a": 0, "b": 1, "result": -1},
    {"a": 1, "b": 2, "result": -1},
    {"a": 2, "b": 1, "result": 1},
    {"a": 2, "b": 2, "result": 0},
    {"a": 3, "b": 2, "result": 0}
]
JSON);

// Make the table pretty
$settings = call !TABLE->getDefaultSettings();
$settings->classHeader = &str_concat($settings->classHeader, " table-dark");
$settings->classBodyCell = &str_concat(
    $settings->classBodyCell,
    " text-center"
);
$settings->classBodyFirstCol = &str_concat(
    $settings->classBodyFirstCol,
    " text-center table-dark"
);
$settings->classHeaderItems = &str_concat(
    $settings->classHeaderItems,
    " w-25"
);

// Create the table
$table = call !TABLE->create($settings);

call $table->setHeader(["", "A", "B", "Result"]);

each $test in $tests {
    // This is where the function runs
    $result = &compare($test['a'], $test['b']);
    
    call $table->addRow(
        &iff(
            $result == $test["result"],
            "✔️",
            &str_concat("❌ (", $test["result"], ")")
        ),
        $test['a'],
        $test['b'],
        $result
    );
}

call $table->show();

Result:

Result of running the example

This is an example of how the table will look like.

\$\endgroup\$
1
\$\begingroup\$

Desmos, 15 bytes

f(a,b)=sgn(a-b)

Try It On Desmos!

\$\endgroup\$
1
\$\begingroup\$

Binary-encoded Golfical, 27 bytes

Hexdump of binary encoding:

00 30 07 15 14 14 14 14 17 17 0c 01 14 14 14 17
00 01 02 01 1c 15 3b 4e 23 26 1d 

Original program image:

enter image description here

Magnified 40x with RGB colors labeled:

enter image description here

Based on my answer here.

\$\endgroup\$
1
\$\begingroup\$

Common Lisp, 28 bytes

(lambda(a b)(signum(- a b)))
\$\endgroup\$
1
\$\begingroup\$

MMIX machine language, 2 instrs (8 bytes)

For s64:

00000000: 30000001 f8010000                    0¡¡¢ẏ¢¡¡

Disassembled:

    CMP $0,$0,$1        // x = x <=> y
    POP 1,0             // return x

For u64:

00000000: 32000001 f8010000                    2¡¡¢ẏ¢¡¡

Disassembled, the only change is the first instruction is now CMPU.

For f64:

00000000: 01000001 f8010000                    ¢¡¡¢ẏ¢¡¡

And this time the first instruction is FCMP.

All of these functions are excellent candidates for inlining.

\$\endgroup\$
1
\$\begingroup\$

Ly, 14 bytes

nns=!rlLfp2*,*

Try it online!

Longer than I'd hoped, but I couldn't find a shorter way to do it given the instructions available...

nns             - read in two numbers, stash the second one away
   =!           - are they equal? invert result so "0" is set for equal
     rl         - reverse stack, recover the second number
       L        - do "less than" comparison
        fp      - delete the first number (it's still on the stack)
          2*,   - converts "0" to "-1" and "1" stays "1"
             *  - multiple by "are they equal?" val to get "0" as needed
\$\endgroup\$
1
\$\begingroup\$

Python, 31 Bytes

lambda a,b:(x:=a-b)and x/abs(x)

-7 Bytes from The Thonnu.

abs(x) gets the absolute value of x (think of it as scissors that can cut the - sign off numbers).

\$\endgroup\$
2
  • \$\begingroup\$ I would recommend using either Try it online! or Attempt This Online! before submitting an answer. It allows you and others to test your program, and you can also copy the submission from there instead of writing it up yourself. \$\endgroup\$
    – The Thonnu
    Apr 6 at 13:18
  • 1
    \$\begingroup\$ Also, 32 bytes, or 31 bytes in Python 3.8+ \$\endgroup\$
    – The Thonnu
    Apr 6 at 13:21
1
\$\begingroup\$

PARI/GP, 3 bytes

cmp

Attempt This Online!

\$\endgroup\$
1
\$\begingroup\$

Common Lisp, 24 bytes

(compose'floor'signum'-)

This is the functional composition of functions floor, signum and -.

The signum function computes the sign and return the expected values, but for floating point values the result is also a float, either -1.0, -0.0, +0.0 or +1.0. That's why the floor operation is used to convert the result back to an integer.

\$\endgroup\$
1
\$\begingroup\$

Gaia, 1 byte

C

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Thunno 2, 1 byte

Attempt This Online!

Built-in. Inputs in reverse order, b then a. Requires \$\le 2.2.0\$, as this was changed in Thunno \$2.2.1\$.

Thunno 2, 2 bytes

Same as most other answers: sign of a-b. Inputs in normal order, a then b. Works in all versions.

Attempt This Online!

\$\endgroup\$
0
\$\begingroup\$

Arturo, 7 bytes

compare

Try it

Builtin.

(In Arturo, <=> is the between operator. 5 <=> 1 10 would return true.)

Arturo, 33 bytes

$[a b][(a>b)?->1->(a=b)?->0->0-1]

Try it

Non-builtin.

Arturo doesn't have a sign function or integer booleans, so I couldn't think of anything better than a nested ternary.

\$\endgroup\$
0
\$\begingroup\$

Factor, 9 bytes

[ - sgn ]

Try it online!

Sign of difference. Factor has a <=> word, but it returns +lt+, +eq+, and +gt+ symbols.

\$\endgroup\$
0
\$\begingroup\$

Desmoslang Assembly, 5 Bytes

I-IOT

I belive you had said that any negative or positive number works.

\$\endgroup\$
1
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.