Live data from Hacker News

Almost Always Unsigned

graphitemaster.github.io

61–70 of 103 posts

Re: Almost Always Unsigned

#61
While I'm a fan of unsigned (size_t mostly) there have been a few times when the tax for converting them to float was shockingly high:

https://godbolt.org/z/96T4jTshc

1-2 instructions for signed vs 11 including a branch for unsigned.

(in times like these I found casting to signed first preferable)

Re: Almost Always Unsigned

#62
> 0x7ffffffffffffffff. The typical argument is that such a value would be “pathological”. Not only is this argument incorrect, it’s even more dangerous which we will see later.

The only later thing I see that's somewhat relevant to that appears to be dealing with 32 bit overflow? That doesn't prove the argument incorrect.

No current OS I'm aware of lets you have a size_t that goes over 2^63. I doubt any OS will ever allow it. It makes things easier if virtual memory is capped to 2^62 or so, and if anyone really ends up with a use case for more I expect them to switch to 128 bit numbers.

Re: Almost Always Unsigned

#63

In a language that has arbitrary precision integers, you'd pretty much never want them unsigned, or even to have signed and unsigned flavors. Whether unsigned or signed is better is a matter that is a combination of personal opinion and the quirks of a given systems programming fixed integer language. The trade-off reasoning would be different, for instance, in a language that requires implementations to provide two'…

I believe the main issue lies in most programming languages lacking theorem proving capabilities to prove the safety of integer operations.

The safety conditions for unsigned arithmetic:

  Ensure y+x ≤ INT_MAX.
  If x ≤ UINT_MAX-y, then x+y evaluates correctly:
  ∀x∀y(x ≤ UINT_MAX-y → ∃z(z = y+x))

  Ensure y-x ≤ INT_MAX.
  If x≤y, then y-x evaluates correctly:
  ∀x∀y(x≤y → ∃z(z = y-x))
The safety conditions for signed arithmetic:

  Ensure INT_MIN ≤ y+x and y+x ≤ INT_MAX.
  To avoid overflow or underflow, first compare x to 0.
  In the case x≤0, INT_MIN-x cannot underflow, and y+x cannot overflow. If y compares greater than INT_MIN-x, then y+x evaluates correctly.
  In the case 0≤x, then INT_MAX-x cannot overflow, and y+x cannot underflow. And if y compares less than INT_MAX-x, then y+x evaluates correctly.
  ∀x∀y((x≤0 ∧ INT_MIN-x≤y)∨(0≤x ∧ y≤INT_MAX-x) → ∃z(z = y-x))

  Ensure INT_MIN ≤ y-x and y-x ≤ INT_MAX.
  To avoid overflow or underflow, first compare x to 0.
  In the case 0≤x, INT_MIN+x cannot underflow, and y-x cannot overflow. If y compares greater than INT_MIN+x, then y-x evaluates correctly.
  In the case x≤0, INT_MAX+x cannot overflow, and y-x cannot underflow. If y compares less than INT_MAX+x, then y-x evaluates correctly.
  ∀x∀y((0≤x ∧ INT_MIN-x≤y)∨(x≤0 ∧ y≤INT_MAX+x) → ∃z(z = y-x))
The programmers that prefer unsigned arithmetic intuitively feel the greater simplicity compared to signed integers, but without any theorem proving, I agree that your assumption of small integers strongly supports signed integers.

Re: Almost Always Unsigned

#64
Sometimes I need the 5th element of something, regardless of what it is. Sometimes the 5th element of something needs to be compared to the 35th element of something else. Both cases require the use of direct and/or derived indexing. This is often the case in low-level code.

As for why signed is default, this may have to do with the error handling in C. By convention, if a called function hits a failure mode, it returns a negative integer. If a function succeeds but with a caveat or warning, it returns a positive integer. Unqualified success returns zero. Hence why idiomatic C functions typically return int, not unsigned int, and we've been stuck on this convention ever since.

Re: Almost Always Unsigned

#66
post #64

Sometimes I need the 5th element of something, regardless of what it is. Sometimes the 5th element of something needs to be compared to the 35th element of something else. Both cases require the use of direct and/or derived indexing. This is often the case in low-level code. As for why signed is default, this may have to do with the error handling in C. By convention, if a called function hits a failure mode, it retu…

It's a bit simpler. C didn't have `unsigned` until 1976.

Re: Almost Always Unsigned

#68
post #20

Earlier quoted context omitted.

I liked how the discussion of 'delta = x - y' moved right on to how really you usually want delta = abs(x - y), so let's talk about that instead... Even beyond Stroustrup, Dijkstra, and Google, this whole panel of C++ luminaries agrees to prefer signed types and explains pretty clearly why: - 12:12-13:08 - https://www.youtube.com/watch?v=Puio5dly9N8#t=12m12s - 42:40-45:26 - https://www.youtube.com/watch?v=Puio5dly9N8…

Thanks for the excerpts! I was trying to understand the reasoning, which seem to just be in the 2nd excerpt: - The rules of signed/unsigned are complicated and there is too much auto-conversion - does that mean languages that make this more explicit means this is fine? It just seems ideal to have stronger typing. - It is mentioned that you can initialized an unsigned int to "-2" - but that presumably could also be fi…

I still don't like having explicit conversions everywhere like in rust. Either you're not thinking too hard about it and the explicit conversions are not really doing anything for you, or you are, meaning you need to be reasoning about it every time and justifying why it can never fail and/or injecting error handling. I would be a much happier rust user if index/length types were are i64 and we relegated unsigned types to serialization almost exclusively. I have other gripes for unsigned types btw, those are just my complaints why explicit casts are not a panacea.

Re: Almost Always Unsigned

#69
post #18

Earlier quoted context omitted.

signed overflow (or underflow) is frequently undefined behavior. (often because it's undefined in C) unsigned is frequently defined. (often because it's defined in C) tough choice. (honestly I just lean towards "over/underflow should raise unless explicitly allowed", the ratio of unintended to intended-and-fully-checked overflow behavior is almost certainly FAR beyond 100:1)

Of course unsigned is defined. That's besides the point. The point is: how often in your code, do you expect 1 minus 2 to equal a very large number, vs. the number -1.

I honestly feel like it is the point at which you find field arithmetic intuitive that demonstrates you have finally understood computers.

Re: Almost Always Unsigned

#70
post #60
post #3

> for (size_t i = size - 1; i Erm... just because you can, doesn't mean you should. Also, what if you want to go down to something other than 0?

Post-increment inverts to pre-decrement, but for-loops don't support proper syntax sugar for pre-decrement. for(size_t i = 0; i 0;){ i--; // loop body }

Put the "--" in the condition. It's less ugly.
Post reply on HN