Live data from Hacker News

Almost Always Unsigned

graphitemaster.github.io

41–50 of 103 posts

Re: Almost Always Unsigned

#41
post #20

Earlier quoted context omitted.

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…

Considering Rust doesn’t have such nonsense, I’m inclined C/C++ have utterly broken generations of programmers. In what world is using a signed value to index a normal array a good idea? Makes for horrible footguns like: history[counter % SIZE] = … (One cursed day counter rolls over, becomes negative, and an out-of-bounds write occurs) Everything went South as soon as we broke the abstraction of arrays and treated th…

> Makes for horrible footguns like:

> history[counter % SIZE] = …

The footgun here is that the “modulo” operator does not actually calculate the modulo in C. In Python, this works correctly for negative values.

Re: Almost Always Unsigned

#42
> Where unsigned does benefit here is when these are used as indices into an array. The signed behavior will almost certainly produce invalid indices which leads to memory unsafety issues. The unsigned way will never do that, it’ll stay bounded, even if the index it produces is actually wrong. This is a much less-severe logic bug, but can still be used maliciously depending on context.

The argument for signed often goes that it's easier to detect an invalid operation on an index by checking for bounds, or the higher probability of segmentation faults, than if your index is always “valid”. Granted, even with signed your invalid operation might still give a valid index. Simply put, seeing a negative index in your debugger is an obvious red flag you lose with unsigned.

In general I want to complain about the idea that a subtle bug is less severe than an obvious bug. Obvious bugs can be caught automatically or manually and are therefore less severe than subtle ones. This is a mistakes all students do btw. Segmentation faults are your friends!

Re: Almost Always Unsigned

#44
post #43
post #37

Earlier quoted context omitted.

It works perfectly fine for size = 0?

They meant x = 0.

Not sure if they meant that, since the “awful” was referring to the original loop that had no x, but the generic solution in that case is:

    for (size_t i = size; i—- > x;)

Re: Almost Always Unsigned

#45
post #41

Earlier quoted context omitted.

Considering Rust doesn’t have such nonsense, I’m inclined C/C++ have utterly broken generations of programmers. In what world is using a signed value to index a normal array a good idea? Makes for horrible footguns like: history[counter % SIZE] = … (One cursed day counter rolls over, becomes negative, and an out-of-bounds write occurs) Everything went South as soon as we broke the abstraction of arrays and treated th…

> Makes for horrible footguns like: > history[counter % SIZE] = … The footgun here is that the “modulo” operator does not actually calculate the modulo in C. In Python, this works correctly for negative values.

> In Python, this works correctly for negative values.

They’re both “broken” in different ways. Arguably C’s brokenness is more apparent and less useful but Python also has footguns: C uses truncated division for its “modulo” so the remainder has the sign of the dividend, Python uses floored division so the remainder has the sign of the divisor instead.

The wiki page for modulo has a pretty extensive page on the subject.

Re: Almost Always Unsigned

#46
post #28

There was a related article from the other side of the debate a while back: https://news.ycombinator.com/item?id=47989154 It’s pretty sad that after all these years, dealing with fixed size integers is still so complicated. Yes, many of the problems are specific to low level languages with undefined behaviour and numeric for loops. But the issue of subtracting two numbers and possibly having an underflow is both comm…

For the stuff I do, I rarely use signed types. I find the example of finding difference between two numbers to be really silly. I don’t recall ever having to do anything like that, for a simple reason: I design my code so that I always know which number will be larger than the other. Let’s actually talk more about this max - min example. Let’s say you calculated this delta. What is it useful for in your code? Typical…

> I design my code so that I always know which number will be larger than the other.

That’s good discipline, but with better tooling and languages such conditions could be enforced by the compiler such as with Dafny. But Dafny is overkill for those who only want to avoid underflow and overflow without verifying everything else about their program…

That’s what I mean when I lament the state of affairs for underflow. Subtracting numbers is such a basic operation, but for the tech stacks used by most teams it still requires a level of care to avoid errors or corruption. Our industry is building million line behemoth codebases but these basic operations still have to be designed around.

> Typically, in real algorithms, what you want is to not only know the delta, but also which number is larger.

I agree with you, and the article was more concerned about avoiding underflow than actually having a practical use for its various example idioms.

What I’d actually prefer is a guarantee that subtracting two (X) bit numbers results in an (X+1) bit number. A sort of compile-time-checked bignum implementation where the programmer never thinks about the integer sizes or which number is larger until the final step of a calculation. Obviously this gets wasteful when multiplying numbers several times. EDIT: and I’m referring to an abstraction of integer bit sizes in a programming language, I’m not expecting the instruction set or hardware to have arbitrary sized registers.

Re: Almost Always Unsigned

#48

Should be (2022) apparently - surely if HN can automatically screw up titles for various reasons, we can have it add dates automatically [and sometimes get those wrong] too? DanG ? > for instance C and C++ leave signed integer wrap undefined I'm pretty sure the way to write what was meant here is "C and C++ leave signed integer overflow undefined". Wrapping would be a definite choice. Overflow is the situation we're…

https://odin-lang.org/docs/overview/#integer-overflow > For signed integers, the operations +, -, *, /, and << may legally overflow and the resulting value exists and is deterministically defined by the signed integer representation. Overflow does not cause a runtime panic. A compiler may not optimize code under the assumption that overflow does not occur. For instance, x < x+1 may not be assumed to be always true.

You've linked Odin's documentation. It's good to have documentation, but, because Matt Godbolt is a nice guy we can just try it out and see for ourselves

https://odin.godbolt.org/z/4WezGr7nK

In Odin, Dividing the 16-bit signed integer -32768 by -1 results in taking SIGFPE. Maybe it's not documented to do that, but that's what happens.

Re: Almost Always Unsigned

#49
post #42

> Where unsigned does benefit here is when these are used as indices into an array. The signed behavior will almost certainly produce invalid indices which leads to memory unsafety issues. The unsigned way will never do that, it’ll stay bounded, even if the index it produces is actually wrong. This is a much less-severe logic bug, but can still be used maliciously depending on context. The argument for signed often g…

> btw. Segmentation faults are your friends!

Indeed. I can't count how often I told people that segmentation faults are among the best kind of errors. One has the complete state and not a log message missing 90% of relevant info.

Of course you don't want processes to crash all the time and spend time on writing gigabytes of data onto disk. But for the rare failure it's a good thing to have.

Re: Almost Always Unsigned

#50
The article over-downplays the need for sentinel values. Surely Rust has Option that also spreads into C++ these days as std::optional. But for plain C using -1 or negative values to denote sentinels or error code is rather nice idiom.

The argument will be more valid if array indexes will be 1-based like in Fortran/Matlab/Julia as then 0 becomes extremely nice sentinel values. But C is C and needs -1.

Post reply on HN