Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Unfortunately the working group rejected the author's proposal that signed integers have wrapping behavior, so signed overflow/underflow remain undefined:

> Status-quo If a signed operation would naturally produce a value that is not within the range of the result type, the behavior is undefined. The author had hoped to make this well-defined as wrapping (the operations produce the same value bits as for the corresponding unsigned type), but WG21 had strong resistance against this.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p090...



I consider this less of a shoo-in, in my opinion, since it's more complicated to implement this in the case where your integer type doesn't actually match the register size of your hardware (which is true with int on LP64). With unsigned integers you can just mask off the extra bits when overflow occurs, but with signed integers this is more complicated. Having this be implementation-specific behavior might be useful, so you don't have undefined behavior in your program, but I am generally of the opinion that overflowing an int is usually a bug rather than a feature.


> it's more complicated to implement this in the case where your integer type doesn't actually match the register size of your hardware (which is true with int on LP64)

If you do arithmetic on a 32 bit register on AMD64 you still get 32 bit wraparound don't you?


This is generally an issue with loops that involve memory accesses, because it causes annoying problems when optimizing: https://gist.github.com/rygorous/e0f055bfb74e3d5f0af20690759...


I wonder, would using int_fast32_t be a viable solution to the problem described above?


Making signed overflow/underflow trapping behavior would also be acceptable.

I don't think the performance improvements are worth the security vulnerabilities, besides which the loop variable issue could be solved by adding `auto` and promoting its use - then the compiler can choose the loop counter type appropriate for the machine since the int-as-64-bit ship already sailed.

(Clang implements this as `__auto_type` and it harmonizes with C++; I've done #define auto __auto_type in projects and it cleans up a lot of C code quite nicely. I'm somewhat surprised no one has offered it as a solution since it is already a reserved keyword).


> the loop variable issue could be solved by adding `auto`

I'm not sure how this helps? The compiler doesn't get to choose based on the machine register width – it's bound to choose based on the type of the expression, which is defined by C/C++ integer promotion rules. The expression `0` is an `int` regardless of the context in which it appears, and thus will be an `auto` variable initialized to `0`.

You're better off using `ptrdiff_t` than `auto` for loop indices if you want something sized to native register width.


> The expression `0` is an `int` regardless of the context in which it appears

Not totally true. In C, 0 is a special value that can be an integer or a null pointer literal depending on the context.


> I don't think the performance improvements are worth the security vulnerabilities

This is C++ you're talking about, that's never the right trade-off :P

> the loop variable issue could be solved by adding `auto` and promoting its use - then the compiler can choose the loop counter type appropriate for the machine since the int-as-64-bit ship already sailed

I'm not sure this completely solves the problem, since often you're doing something like this (I know, I know, iterators are the way to do this, but people do write code like this):

  for (int i = 0; i < some_container.size(); ++i) {
  	// do something
  }
Auto doesn't really help here, because if you do something like

  for (auto i = 0; i < some_container.size(); ++i) {
  	// do something
  }
the compiler can't look at your code and say "i really looks like it was meant to be a 64-bit index", it needs to be stupid and deduce it to be an int or at least behave as an int would. Unless I'm misunderstanding what you're trying to say?

> (Clang implements this as `__auto_type` and it harmonizes with C++; I've done #define auto __auto_type in projects and it cleans up a lot of C code quite nicely. I'm somewhat surprised no one has offered it as a solution since it is already a reserved keyword).

Well, you're technically breaking code that actually uses auto the way it was intended to be used, though of course since it's so useless this kind of code is very rare.


> Making signed overflow/underflow trapping behavior would also be acceptable.

You can just add the right compiler flag if this is the right tradeoff for you application (it isn't for everyone).


I would guess that the reasoning (which I agree with) is that defined signed overflow precludes a standards-compliant compiler both from making certain optimizations (made under the assumption that overflow will not occur) and from performing error checking (e.g. -ftrapv), and enables the writing of confusing code (which unsigned arithmetic would make more clear).

Forced to choose, I'd rather see signed overflow be required to abort the program or throw an exception than wrap.


The next best thing would be for C++ to adopt something like Zig's modulo-wraparound operators: +% -% *% /%. Writing a proper implementation of signed overflow in portable C++ is annoyingly complicated right now, for something that can be useful so often.

Or better yet, some way to specify an expression context for all arithmetic operators contained inside - the default could still be undefined, but you could then also explicitly request wraparound, saturation, or fast-fail. Something like C# checked(), basically (which gives you fast-fail), but covering all useful cases.

https://docs.microsoft.com/en-us/dotnet/csharp/language-refe...


I bet there is already something like that available as library.


There is, e.g.:

https://github.com/dcleblanc/SafeInt

But IMO something like this should be a language thing, just so that it's not substantially harder or more verbose to use operators with explicit wraparound or trap.


What we need is a winner in the C++ package manager wars, then adding such dependencies wouldn't be such a big issue.


That's bad for code that can handle overflow and do something reasonable, like switch to bignum integers.

If wrapping takes place, overflow can be detected in-line; no funny control paths via exceptions or whatever.

Without the spectre of UB hanging over overflows, efficient code for dealing with overflows can also be ISO-defined at the same time.


> That's bad for code that can handle overflow and do something reasonable, like switch to bignum integers.

I see that as an argument for throwing exceptions or setting a flag on overflow, not for silently wrapping to negative values.

Defining signed overflow means that I can't enable runtime checking of it without erroneously flagging intended cases of overflow. To deny that to support the tiny percentage of developers who write bignum libraries strikes me as a poor tradeoff.

(Simply checking for a sign change isn't sufficient for implementing bignums anyway. It's trivially possible to multiply a large positive number by a small positive number and have it wrap not only past negatives back to positives, but to a number larger than the input.)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: