Hacker News new | ask | show | jobs
by umanwizard 2588 days ago
> I think the answer is that, in code, we type in decimal: 0.1+0.2

I think you are exactly right. People think of decimals as being the "actual", "primary", "fundamental" numbers, and binary as being an imperfect representation of those. Whereas in reality, both binary and decimal are imperfect representations of rational numbers, and we only think of decimal as being more fundamental because of our writing system.

> Fair enough about floats, but why about arbitrarily large integers

How exactly would you represent them? The best way I can think of is:

    struct BigInt {
        int64_t first_64;
        char *data; // pointer to extra, dynamically allocated data
        int data_len;
    };
This would allow you to avoid doing a dynamic allocation for the most common case of being under 64 bits. And the addition algorithm would probably special case that too, and look something like this:

    BigInt add(BigInt x, BigInt y) {
        if (x.data_len == 0 && y.data_len == 0) {
            int64_t new_val = x.first_64 + y.first_64;
            if (overflow_signaled()) {
                return add_slow_path(x, y);
            }
            return {new_val, 0, nullptr};
        } else {
            return add_slow_path(x, y);
        }
    }
As you can see, there is a ton of complexity here, even just for the simplest possible case. Replacing what was before literally just one instruction, e.g. addq %rbx, %rcx . Also, each number is represented by a 20-byte struct now instead of 8. So now each 64-byte cache line can fit only 3 values instead of 8. Because of all this, it would be dramatically slower.

This is just for the easiest case of no overflow! If you overflow and have to then go allocate memory dynamically and loop over it, it would of course be even worse.