Skip to content(if available)orjump to list(if available)

Nearly All Binary Searches and Mergesorts Are Broken

jsnell

motorest

> (2006)

In the very least,this should feature in the title. In fact, previous submissions from over a decade ago also feature year in the title.

Otherwise it conveys the idea that this is some major epiphany.

xmcqdpt2

Also, I didn't know about this specific bug but I spotted it almost immediately while reading the code. This is not because I'm some kind of 10x programmer but because Integer.MAX_VALUE bugs while processing arrays are actually fairly common in java programs in 2025, I fixed one a few weeks back and it's something I look for in code reviews.

I guess it would have been surprising in 2006?

brabel

In 2006, if you tried to create an array with 2^30 elements you would just get an OutOfMemoryError almost anywhere.

joshka

The problem with this article's name is that we're in an era where actually checking whether "nearly all sorts are broken" against all publicly available implementations of binary searches would be almost feasible, but that's not actually what the article is claiming.

> Moreover, to be really certain that a program is correct, you have to test it for all possible input values, but this is seldom feasible.

This is incorrect. Generally it's just a little excessive to try to solve the halting problem in a library's unit tests ;). You don't have to test a program for all possible inputs, only for all materially unique state transitions. In a binary search, each variable can be zero, one, some(*), max, overflowed. The combination of these is not infinite as the values of some that cause different behavior is much more reasonable when grouped into chunks of the same behavior.

PaulKeeble

It was certainly possible to run the algorithm on 16GB of array before the moment when it happened but did the original developer have that sort of space on their desktop at the time? Possibly not.

If a unit test only runs on a server and not on the laptops of the developers then its not going to be written, whereas ideally someone should write a test that is tagged to only run on the server but that is a lot of extra work if that isn't a common thing on a project. Even now I would be quite wary of producing a max size input test for an array of ints and especially objects, that is going to raise some questions due to being a slow test and a highly resource consuming one.

If I was worrying about the same in aerospace programming however then no question that test would get written and we would ensure it got run on the hardware that it was designed to run on. In typical business software its less common to run potentially very long running tests and simulations of the machines states everyone wants to go faster than that especially for the sake of a max input test.

miohtama

Also you can have checked math, like in Rust, and automatically just crash when you overflow a variable.

In this case, it's not a bug (cannot get incorrect result) but an unsupported input.

xmcqdpt2

The correct way to test that code is to write a version that doesn't take an actual array but an index -> int function, then you wouldn't need to instantiate the array at all.

poincaredisk

Small nitpick: you don't need 16GiB, 2^31 bytes is "just" 2GiB. Doesn't contradict your point though.

null

[deleted]

MaxikCZ

Are you quantizing information?

bhouston

It makes the case that we need Math.mean or Math.avg function that we can use in these cases rather than than reinventing it.

Basically we should favor using built in functions for as much as possible because those should be reliable and tested more than ad hoc code we write. And compilers should optimize those built in functions well so there is no extra cost in using them.

heinrichhartman

    int mid =(low + high) / 2;
> Specifically, it fails if the sum of low and high is greater than the maximum positive int value (2^31 - 1).

I would really challenge calling this kind of effects "bug" or "breakage".

It's like calling Newtons law of gravity broken, because it's not accurate at predicting how galaxies move.

Things are engieered and tested for a certain scale.

Knowing which tools to use at which sacle is part of the craft of engineering.

alanfranz

> certain scale

Make it explicit. If the array is too large, throw an IllegalArgumentException. Document the limit. Then I agree with you.

Otherwise, if an allowed input crashes the program at runtime with a random exception, I respectfully disagree.

brabel

Then you should absolutely stay away from C :)

ajuc

The problem is that it's not that much harder to make it work for all the valid inputs.

Not doing that is not good enough.

Another example is summing lots of floats naively instead of using Kahan's algorithm.

It's like we had a theory of gravity that doesn't work on Mars because we have unnecessary division by (m-Mars' mass) in our laws :) It wouldn't be good physics.

secondcoming

I would disagree. The inputs to these functions are user controlled and so can be forced to break, whereas humans cannot change how gravity works.

borntyping

Nearly all binary searches and mergesorts are broken in languages with bounded integers.

3ple_alpha

No they're not. If you're using an array with length over a billion in Java, your code stinks already before you start using binary search.

poincaredisk

I'm not a Java programmer, but how would you load of a 1GB file into memory? I assume read returns some kind of an array.

Also big arrays being (supposedly) a coffeee smell doesn't mean that code handling them improperly is not buggy.

angus_gh

Java's arrays use a signed 32-bit int as their length, so the longest they can be is about 2 billion elements.

If your code has arrays over a billion elements, then it will fall over the moment someone inputs slightly larger data

danvonk

Relational databases often require searching and sorting gigabytes of data to answer queries (sometimes larger than RAM if e.g. k-way merge sort is used) so it doesn't seem that far-fetched, especially given that there are database systems written in Java.

null

[deleted]

Ameo

A cynical takeaway from this is that it's hard to write good code and it doesn't really matter if you do or not.

Most code at every company I've worked at and project I've built is littered with technical incorrectness and buggy half-measure implementations. It's human nature or time pressure or something like that, but the application continues providing business value despite that.

nritchie

Isn't the larger point of this article that errors like this can sneak in and remain dormant for years? Even if this example is old, isn't this lesson still relevant? Expecting that we are now immune from this class of errors because it is not 2025 and not 2006 is hubris. Hubris rarely ends well.

sonu27

Title needs updating with the year 2006

usr1106

I often think AI is mostly crap, wasting a lot of energy for very questionable benefits. But could/should this repetitive task of reminding submitters to follow the submission guidelines and add the year to submissions of old articles be automated?

pdimitar

I would agree, though why would you need AI for that is an open question.

usr1106

What algorithm would you suggest to find the year in an arbitrary submission? Of course AI is not a very clearly defined term, more difficult problems certainly exist. I was just thinking of the case the submission contains several dates or none at all and still several hints a human would take into consideration get checked.

Of course some minimal implementation without AI techniques could already handle many cases. My AI suggestion was not death-serious ;)

sonu27

A simple crawler would have been able to detect it’s from 2006. Perhaps a reminder should be added if the year is not recent

xigoi

So previously it worked for arrays up to length 2³⁰ and now it works for arrays up to length 2³¹. Is it really that much of a difference? Wouldn’t it be better to change the indexing type to a 64-bit integer?

ddxxdd

The difference is that we know for a fact that the proper implementation works for integers up to 2^31, whereas the improper implementation deceived us into thinking that the code would work in situations where the code actually doesn't work.

I find it valuable to understand when my code will crash.

foldr

Article was written in 2006 when 32-bit architectures were dominant.

I suppose the issue is moot now on 64-bit architectures, as the difference between 2^62 and 2^63 isn't relevant. (2^62 bytes is 4611686 terrabytes.)

im3w1l

Spelling it out like that sure gives some perspective - It's a frighteningly low number! They sell 2tb microsd cards nowadays. I bet you could fit 2^64 bytes in a shoebox. So I think uint64 might eventually be insufficient as a size type.

Edit: Well not quite, more like a small closet.

tliltocatl

Almost makes you think RISC-V was right with 128-bit extension. On the other hand, exabyte-scale memories might one day be possible, but would it still be useful to maintain single-address-space illusion for these?

eesmith

The use of:

   int high = a.length - 1;
tells us that a.length-1 is supposed to fit into an int, so there is no need to handle 2³¹ or larger.

rob_c

Yep. No story here, feel free to move on...

MattPalmer1086

Not really. Arrays are limited to an int in size. You would be using more memory for the calculation and have to cast the value down to a 32 bit value to use as an array index.

Or you could just write the code so it isn't vulnerable to integer overflow.

touisteur

Which is sad (array size limited to an int) and has always annoyed me, coming back from Ada where the index of an array is just another discrete type (including boolean, enum type, and ranged integers).

rob_c

Ideally the index should support int64 with int being a synonym on a 64bit platform by default.

If not yes frankly you're into such unexpected behaviour territory that you should check your whole codebase rather than rely on stuff working just because it compiled.

And we all know how everyone loves writing and understanding integration tests... (I personally do but most problems in the industry wouldn't be a thing if more people stopped to write them)

bobmcnamara

size_t, you mean?

djmips

If you did a lot of stuff on 8 bit systems you ran into this very early and often.

rep_lodsb

Not in assemly, where it's trivial to shift right with carry.

akam4n4n

maybe dumb, but what happens when low and high > unitMAX/2

shift left is worse in that case right?

poincaredisk

It's not possible, because low and high are of type int, which is always lesser than uint_nax/2