6 ms·
So is sqrt(-1) but that doesn't mean sqrt() shouldn't exist. Just don't call add() with arguments that will overflow.
by first_amendment 9y ago
So is sqrt(-1) but that doesn't mean sqrt() shouldn't exist. Just don't call add() with arguments that will overflow.
- devrandomguy 9y agoSo, sanitize your `/add?a=1&b=2` endpoint inputs by making sure that they don't add up to something greater than... wait a sec. Actually, let's just return an unsigned int. No wait! We might get a negative number as input. Hmm, can I bitbang my way out of this? Can I be sure that the signed ints are two's complement? Man, these languages are seriously !!fun!!
- first_amendment 9y agoThere will always be syntactically valid yet semantically invalid statements in any grammar. Undefined behavior isn't bad, it's a unavoidable consequence of all languages, programming or otherwise. Grammatical constructions that have no meaning will always exist. If you want to make sure your callers are obeying the interface contract, use assert() to not incur runtime overhead in production and guard against programming error: int add(int a, int b) { assert(!__builtin_sadd_overflow(a, b, NULL)); return a + b; }
- icebraining 9y agoThere will always be syntactically valid yet semantically invalid statements in any grammar. Really? Why? And can you give an example of that in, say, Python?
- first_amendment 9y ago(-1) ** 0.5 Why define something that has no meaning? Just a waste of cycles, CPU and brain.
- pavanky 9y agoI am assuming this is python. This fails in python2 but works (returns complex number) in python3.
- geofft 9y agoThis is well-defined: >>> (-1) ** 0.5 Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: negative number cannot be raised to a fractional power Any conforming implementation of Python 2.7 must raise ValueError. Why define it? So that a programmer can catch ValueError.
- first_amendment 9y agoAs I said in another thread, trusting a buggy program to debug itself is futile. Effectively handling unintentional ValueError (or IndexError, TypeError) is not practically possible. For one, there's a good chance the program's persistent shared state is in an inconsistent state when those unintentional exceptions happen. Better to just abort().
- striking 9y agoThey can choose to do just that. But they're also afforded a little more flexibility with this approach. A developer could use this to serve up a stack trace rather than just crashing the server, for example.
- first_amendment 9y agoYou can always restart the process after an abort(). Continuing to run a server with persistent in-process state after an unexpected exception is dangerous and you risk corrupting data.
- geofft 9y agoMany servers do not have persistent in-process state, or have in-process state that is robust against bugs in other parts of the program. In particular, in most languages where you need special syntax or data types to access shared state (so, definitely not C or C++, but Python should count), you can isolate all the code that doesn't use this syntax or these types of objects inside a giant try/catch, and know that any misbehavior inside that block of code cannot possibly have affected the shared state.
- xaedes 9y agoprint(type("foo") + "foo") Syntactically correct. Will provoke error during runtime due to invalid semantics.
- cjbillington 9y agoThat's a TypeError though, not undefined behaviour. People are talking about different things.
- xaedes 9y agoTrue. But I was answering this specific to the question of "syntactically valid yet semantically invalid statements" in python.
- stinos 9y agonot incur runtime overhead in production and guard against programming error I'm a huge fan of assertions and use them to document 'programmer screwed up' and to get notified of cases when that happens; I find that a more corect description then 'guarding against'. However we leave assertions on in release builds as well: tracking down cases of UB only happening in release builds (most often due to someone forgetting to initialize something) is hard enough already, and we found that leaving assertions on can help with that. Only in certain hot paths which prove the runtime overhead matters (and there's really not a lot of those) we'll turn them off.
- zurn 9y ago> Undefined behavior isn't bad, it's a unavoidable consequence of all languages Sorry but sounds like you are not familiar with the special meaning of the term "undefined behaviour" in context of C/C++. It means your program may crash or corrupt memory in a way that results in remote code execution. (Or anything else... http://catb.org/jargon/html/N/nasal-demons.html http://catb.org/jargon/html/N/nasal-demons.html )
- deleted 9y ago[deleted]
- geofft 9y agoIt specifically means that the compiler may assume that this case never happens, and do what's convenient to it. In practice, for the addition function above, the convenient thing for any compiler on any reasonable platform is to just let the hardware handle overflow the way the hardware wants to. Signed overflow is undefined so that C as a language is portable to hardware with different ways of overflowing signed integers, and so there isn't a need for a compiler on one platform to implement special behavior. But it's unlikely to crash, corrupt memory, conjure demons, etc. Corrupting memory requires a bit more setup: you do a bounds-check on a pointer in a way that hits signed overflow (or you do some inappropriate casts, or something), you get a result, and you use that result to index into an array. If the way that you got that result involved UB, your bounds check may not be valid. Again, this isn't because the compiler particularly desires to access invalid memory, but because it wants to do the cheapest possible thing that is still correct for all defined behavior.
- fulafel 9y agoCrashing isn't outside the realm of possibility even with the a+b example: trapping and aborting the program may happen if the hardware has trapping overflow, or if the C implementation is safety focused and inserts explicit overflow checks to avert further unsafe things from happening.
- geofft 9y ago> Undefined behavior isn't bad, it's a unavoidable consequence of all languages, programming or otherwise. This is incorrect. "Undefined behavior" is a specific technical term for a scenario in a program that the compiler is permitted by the specification to assume will not happen, for the purposes of optimization. For instance, this code: int silly(int a) { if (a + 5 > a) return 0; return 1; } can be optimized to just "return 0", because, as a human would read it, obviously a + 5 > a. So the spec says that signed integer overflow cannot occur to allow the compiler to optimize this as a human would want the compiler to optimize this. (Whether the spec actually matches human expectations is a good question, but in general it's right, and forbidding all undefined behavior in C and C++ would cause you to miss out on tons of optimizations that you obviously want.) "Undefined behavior" does not mean providing invalid input to a function and getting an exception, or a crash, or a particular error result, if the result is well-defined. For instance, this is defined behavior: >>> import math >>> math.sqrt(-1) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: math domain error because Python defines the behavior that math.sqrt(-1) raises a ValueError and I can reliably catch that exception. If it were undefined behavior, then I wouldn't have any guarantee of being able to catch the ValueError: the Python interpreter might just choose to return 27 if it's more convenient.
- first_amendment 9y agoThanks for the clarifications. Indeed this is what I meant. Undefined behavior in particular isn't unavoidable, though the existence of meaningless statements often is. Python's philosophy of giving well-defined behavior to meaningless code seems wasteful. If your code is unintentionally executing sqrt(-1) (or unintentionally indexing out of bounds, etc) then something is wrong with your program. You don't necessarily need the behavior to be defined if there is a bug in your program. In the case that you want something predictable to happen, better to just abort(). Catching ValueError/IndexError in those cases is futile, how can one trust a buggy program to handle itself? Python using exceptions to signal both runtime errors and programmer errors is a design smell. The former should always be caught and handled, the latter should never be caught and should probably only reasonably abort().
- a_t48 9y agoIt must be nice to live in a world where you don't need to write fast code.
- coldtea 9y agoSo, like what's the case for 99% of programmers?
- fulafel 9y agoSafe semantics don't preclude fast code. See eg Rust, which arguably enables you to write faster code than C++ - because its guarantees let you have confidence in correctness of complex programs involving fine grained shared memory parallelism mutating common data.
- richardwhiuk 9y agoIf you just specify that a < INT_MAX/2 and b < INT_MAX/2, you are fine. If not (and you are providing a general purpose addition), then you need a big number library.
- junke 9y agoYou can also overflow by adding two large enough negative numbers. /tmp/file.c:2:[kernel] warning: signed overflow. assert -2147483648 ≤ i+j; /tmp/file.c:2:[kernel] warning: signed overflow. assert i+j ≤ 2147483647; (tis-analyzer)
- geofft 9y agoSanitize your endpoint to ensure that both a and b are within INT_MIN/2 to INT_MAX/2. After all, you didn't pick INT_MIN; the entire point of a 32-bit integer or a 64-bit integer is that it's more than enough for reasonable purposes. It's not based on a physical constant or anything, it's just convenient and large. If you need a specific number of bits for your purposes that's more than that, use a u128 extension or a bigint type.
- deleted 9y ago[deleted]
- junke 9y agoCL-USER> (sqrt -1) #C(0.0 1.0)