Live data from Hacker News

Unsigned sizes: A five year mistake

c3-lang.org

61–70 of 153 posts

Re: Unsigned sizes: A five year mistake

#62

I don't get it. Is this a parody of poor design decisions? Sure, it's possible to write bugs in C. And if you really want to, you can disable the compiler warnings which flag tautologous comparisons and mixed-sign comparisons (a common reason for doing this is to avoid spurious warnings in generic-type code). But, uhh, "people can deliberately write bugs" has got to be the weakest justification I've ever seen for cha…

The C language does not have any data type that has the property "can't be negative". Signed integers can be negative. The so-called "unsigned" integers of C are integer residues modulo 2^N, which are neither positive nor negative, i.e. these concepts are not applicable to "unsigned" integers. An alternative view is that any C "unsigned" is both positive and negative. For example the unsigned short "1" is the same nu…

Are you claiming that the following program could possibly print "-1" ?

    #include 
    int main() {
        unsigned short a = 1;
        long b = a;
        printf("%ld\n", b);
    }
If not, why?

Re: Unsigned sizes: A five year mistake

#63
post #57
post #20

Earlier quoted context omitted.

> But I would say if you know a value will logically always be >= 0, better to have a type that reflects that. Except that's not quite what unsigned types do. They are not (just) numbers that will always be >= 0, but numbers where the value of `1 - 2` is > 1 and depends on the type. This is not an accident but how these types are intended to behave because what they express is that you want modular arithmetic, not no…

This is true, which means that a language has to be designed from the ground up to deal with these problems or there will always be inscrutable bugs due to misuse of arithmetic results. A simple example in a c-like language would be that the following function would not compile: unsigned foo(unsigned a, unsigned b) { return a - b; } but this would: unsigned foo(unsigned a, unsigned b) { auto c = a - b; return c >= 0…

First, your code is about having unsigned types represent the notion of non-negative values, but this is not the intent of unsigned types in C/C++. They represent modular arithmetic types.

Second, it's not as simple as you present. What is the type of c? Obviously it needs to be signed so that you could compare it to zero, but how many bits does it have? What if a and b are 64 bit? What if they're 128 bit?

You could do it without storing the value and by carrying a proof that a >= b, but that is not so simple, either (I mean, the compiler can add runtime checks, but languages like C don't like invisible operations).

Re: Unsigned sizes: A five year mistake

#64

I hate using languages that only have signed integers. Using integers that can’t be negative fits many problems nicely and avoids the edge case of having to check for negative.

You are perfectly right, but neither C nor C++ nor many more recent languages derived from them have non-negative integers. The so-called "unsigned" integers of C are integer residues, where each value can be interpreted either as both positive and negative or as neither positive nor negative. In any case no "unsigned" value can be said to be non-negative. You have to go back to languages not contaminated by C, like…

I really appreciate your comments in this thread adrian_b. Could you point me at a brief summary of how Ada (or Pascal?) non-negative ints work? What is a compile error, what is a guaranteed run-time error, etc.

Re: Unsigned sizes: A five year mistake

#65

Earlier quoted context omitted.

> Wrapping around is what happens when you decrement the minimum value of any integer type, including signed types. No, signed wraparound is undefined behavior in C, whereas unsigneds are defined to wraparound. If you use -ftrapv, signed wraparound is an immediate abort().

That is right. While C like in many other places fails to define the correct behavior to avoid shaming the processor or compiler makers that fail to provide it, there are only 2 correct behaviors on overflows and underflows, like when incrementing the biggest number or decrementing the smallest number. Both for signed integers and for non-negative integers, the 2 alternatives of correct behavior on overflows and unde…

When I was building my computing stack out of x86 machine code I noticed that even if my high level language only had signed numbers (I'm still pretty brainwashed by C, which leads to the conclusions of OP), I still needed the ISA's unsigned jumps to deal with addresses (which can have the MSB set). So my big "insight" was to name unsigned comparisons "address comparisons".

https://akkartik.github.io/mu/html/mu_instructions.html

Re: Unsigned sizes: A five year mistake

#66
post #3

Systems programmers love to hate on unsigned integers. Generations have been infected with the Java world model that integers have to be pretend number lines centered on zero. Guess what, you still have boundary conditions to deal with. There are times when you really really need to use the full word range without negative values. This happens more often with low level programming and machines with small word sizes,…

Having them available is not the issue, using them for sizes and indices is what causes a lot of tricky bugs.

I find it the opposite. Unsigned integers are intuitive, while signed integers are unintuitive and cause a lot of tricky bugs. Especially in languages, where signed overflow is undefined behavior.

It's pretty rare to have values that can be negative but are always integers. At least in the work I do. The most common case I encounter are approximations of something related to log probability. Such as various scores in dynamic programming and graph algorithms.

Most of the time, when you deal with integers, you need special handling to avoid negative values. Once you get used to thinking about unsigned integers, you quickly develop robust ways of avoiding situations where the values would be negative.

Re: Unsigned sizes: A five year mistake

#67
post #48

Systems programmers love to hate on unsigned integers. Generations have been infected with the Java world model that integers have to be pretend number lines centered on zero. Guess what, you still have boundary conditions to deal with. There are times when you really really need to use the full word range without negative values. This happens more often with low level programming and machines with small word sizes,…

Java doesn't have unsigned as primitive types, because James Gosling did a series of interviews at Sun among "expert" C devs, and all got the C language rules for unsigned arithmetic wrong. Yes I miss them in Java as primitives, however there are utility methods for unsigned arithmetic, that get it right.

Java has char as an unsigned 16-bit integer type. They should have made byte unsigned as well.

Re: Unsigned sizes: A five year mistake

#68
post #25

> But what about the range? While it’s true that you get twice the range, surprisingly often the code in the range above signed-int max is quite bug-ridden. Any code doing something like (2U * index) / 2U in this range will have quite the surprise coming. Alas, (2S * signed_index) / 2S will similarly result in surprises the moment the signed_index hits half the signed-int max. There's no free lunch when trying to che…

The difference is that in the unsigned case you get a seemingly plausible value, and in the signed case you get a negative value which you can be sure is wrong. This is the problem.

[deleted]

Re: Unsigned sizes: A five year mistake

#69
post #3

Earlier quoted context omitted.

Having them available is not the issue, using them for sizes and indices is what causes a lot of tricky bugs.

Why does an unsigned type for sizes or indices fare worse than a signed type? When do I want the -247th element in an array? When do I have a block that is -10 bytes in size?

Because doing subtraction on sizes/indicies is common, and signed handles the case where you subtract below 0. Unsigned yields unintuitive results. i.e, unsigned fails silently. For example, looping to the 2nd to last item in an array or getting the index before the given index.

The source of confusion is that unsigned is a terrible name. Unsigned does not mean non-negative. Its 100% complete valid to assign a negative value to an unsigned, it just fails silently.

If you want non-negative integers, then you should make a wrapper class that enforces non-negativity at compile and runtime.

Re: Unsigned sizes: A five year mistake

#70

Earlier quoted context omitted.

It's not "can't be negative", it's just that the semantics for negativity is wrapping around. And - yes, there are very important use cases for unsigned/modulo-2n/wraparound values. But sizes of data structures are generally _not_ one of those use cases. The fact that the size is non-negative does not mean that the type should be unsigned. You should still be able to, say, subtract sizes and get a signed value which…

That’s definitely not true. Unsigned ints have no “negativity” semantic. Wrapping around is what happens when you decrement the minimum value of any integer type, including signed types. Regardless of the type you use to represent an integer value that cannot legally be negative, you will have to take care not to allow your program to return values lower than zero for things like indices or sizes.

> Unsigned ints have no “negativity” semantic.

They do. The code:

    unsigned x;
    unsigned y = -x;
is well-defined in C and C++. See this discussion on StackOverflow for spec text and reference:

https://stackoverflow.com/q/8026694/1593077

Post reply on HN