digitalmars.D.learn - enforce (i > 0) for i = int.min does not throw
- kdevel (14/14) Jan 27 2018 I would expect this code
- =?UTF-8?Q?Ali_=c3=87ehreli?= (10/30) Jan 27 2018 Looks like a major issue to me.
- kdevel (17/25) Jan 27 2018 test.d
- ag0aep6g (10/30) Jan 27 2018 Wow, that looks really bad.
- Steven Schveighoffer (5/18) Jan 28 2018 This is insane. i > 0 is used in so many places. The only saving grace
- Seb (14/34) Jan 28 2018 FYI/OT: If you need to check the behavior of old compilers, the
- kdevel (28/31) Jan 30 2018 And another one that it does not happen when compiled with
- Steven Schveighoffer (11/47) Jan 31 2018 This is due to integer promotion
- Azi Hassan (26/40) Jan 31 2018 I wonder if it's caused by a comparison between signed and
- Steven Schveighoffer (9/40) Jan 31 2018 No, the answer is, there's a shortcut optimization used by the compiler....
I would expect this code enforce3.d --- import std.exception; void main () { int i = int.min; enforce (i > 0); } --- to throw an "Enforcement failed" exception, but it doesn't: $ dmd enforce3.d $ ./enforce3 [nothing]
Jan 27 2018
On 01/27/2018 06:13 AM, kdevel wrote:I would expect this code enforce3.d --- import std.exception; void main () { int i = int.min; enforce (i > 0); } --- to throw an "Enforcement failed" exception, but it doesn't: $ dmd enforce3.d $ ./enforce3 [nothing]Looks like a major issue to me. But enforce is a red herring there. This prints true with 2.078 as well: import std.stdio; void main () { int i = int.min; writeln(i > 0); // prints 'true' with 2.078 } Ali
Jan 27 2018
On Saturday, 27 January 2018 at 14:49:52 UTC, Ali Çehreli wrote:But enforce is a red herring there. This prints true with 2.078 as well: import std.stdio; void main () { int i = int.min; writeln(i > 0); // prints 'true' with 2.078 }test.d --- import std.stdio; void main () { int i = int.min; auto b = i > 0; b.writeln; auto c = int.min > 0; c.writeln; } --- $ dmd test.d $ ./test true false
Jan 27 2018
On 01/27/2018 03:13 PM, kdevel wrote:I would expect this code enforce3.d --- import std.exception; void main () { int i = int.min; enforce (i > 0); } --- to throw an "Enforcement failed" exception, but it doesn't: $ dmd enforce3.d $ ./enforce3 [nothing]Wow, that looks really bad. Apparently, dmd implements `i < 0` as a `i >> 31`. I.e., it shifts the bits to the right so far that only the sign bit is left. This is ok. But it implements `i > 0` as `(-i) >> 31`. That would be correct if negation would always flip the sign bit. But it doesn't for `int.min`. `-int.min` is `int.min` again. So dmd emits wrong code for `i > 0`. O_O I've filed an issue: https://issues.dlang.org/show_bug.cgi?id=18315
Jan 27 2018
On 1/27/18 9:50 AM, ag0aep6g wrote:Wow, that looks really bad. Apparently, dmd implements `i < 0` as a `i >> 31`. I.e., it shifts the bits to the right so far that only the sign bit is left. This is ok. But it implements `i > 0` as `(-i) >> 31`. That would be correct if negation would always flip the sign bit. But it doesn't for `int.min`. `-int.min` is `int.min` again. So dmd emits wrong code for `i > 0`. O_O I've filed an issue: https://issues.dlang.org/show_bug.cgi?id=18315This is insane. i > 0 is used in so many places. The only saving grace appears to be that int.min is just so uncommonly seen in the wild. I tested all the way back to 2.040, still has the same behavior. -Steve
Jan 28 2018
On Sunday, 28 January 2018 at 19:17:49 UTC, Steven Schveighoffer wrote:On 1/27/18 9:50 AM, ag0aep6g wrote:FYI/OT: If you need to check the behavior of old compilers, the "All" option at run.dlang.io might be helpful. It starts from 2.060 and it works best for consistent output (i.e. without stacktrace pointer). Examples: - https://run.dlang.io/is/IoN3sj (code from the bug report) - https://run.dlang.io/is/LuxUQ5 (code from the bug report, slightly modified) - https://run.dlang.io/is/3R4r1U (simple example of "when was the symbol added to Phobos?") (It's based on Vladimir's regression tester and can be used locally too: https://github.com/dlang-tour/core-dreg)Wow, that looks really bad. Apparently, dmd implements `i < 0` as a `i >> 31`. I.e., it shifts the bits to the right so far that only the sign bit is left. This is ok. But it implements `i > 0` as `(-i) >> 31`. That would be correct if negation would always flip the sign bit. But it doesn't for `int.min`. `-int.min` is `int.min` again. So dmd emits wrong code for `i > 0`. O_O I've filed an issue: https://issues.dlang.org/show_bug.cgi?id=18315This is insane. i > 0 is used in so many places. The only saving grace appears to be that int.min is just so uncommonly seen in the wild. I tested all the way back to 2.040, still has the same behavior. -Steve
Jan 28 2018
On Sunday, 28 January 2018 at 19:17:49 UTC, Steven Schveighoffer wrote:This is insane. i > 0 is used in so many places. The only saving grace appears to be that int.min is just so uncommonly seen in the wild.And another one that it does not happen when compiled with optimization (-O) and also that it does not affect all the ints: --- import std.stdio; void foo (T) () { auto i = T.min; writefln ("%12s: %24X %12s", T.stringof, i, i > cast(T) 0); } void main () { foo!byte; foo!short; foo!int; foo!long; } --- byte: 80 false short: 8000 false int: 80000000 true long: 8000000000000000 true In 32 bit mode: byte: 80 false short: 8000 false int: 80000000 true long: 8000000000000000 false
Jan 30 2018
On 1/30/18 3:37 PM, kdevel wrote:On Sunday, 28 January 2018 at 19:17:49 UTC, Steven Schveighoffer wrote:This is due to integer promotion (https://dlang.org/spec/type.html#usual-arithmetic-conversions). Any operation between two non-integers, first the two operands are promoted to integers. You can see the result here: https://run.dlang.io/is/RAk9tEThis is insane. i > 0 is used in so many places. The only saving grace appears to be that int.min is just so uncommonly seen in the wild.And another one that it does not happen when compiled with optimization (-O) and also that it does not affect all the ints: --- import std.stdio; void foo (T) () { auto i = T.min; writefln ("%12s: %24X %12s", T.stringof, i, i > cast(T) 0); } void main () { foo!byte; foo!short; foo!int; foo!long; } --- byte: 80 false short: 8000 false int: 80000000 true long: 8000000000000000 trueIn 32 bit mode: byte: 80 false short: 8000 false int: 80000000 true long: 8000000000000000 falseMost likely, this is due to the fact that working with longs cannot be done natively by the CPU, so it can't use the same shifting shortcut that causes the issue in the first place. -Steve
Jan 31 2018
On Saturday, 27 January 2018 at 14:13:49 UTC, kdevel wrote:I would expect this code enforce3.d --- import std.exception; void main () { int i = int.min; enforce (i > 0); } --- to throw an "Enforcement failed" exception, but it doesn't: $ dmd enforce3.d $ ./enforce3 [nothing]I wonder if it's caused by a comparison between signed and unsigned integers. import std.stdio; void main () { int zero = 0; writeln(int.min > 0u); writeln(int.min > zero); } $ rdmd test.d true false The same behavior can be observed in C : #include <stdio.h> #include <limits.h> int main(void) { int zero = 0; printf("%d\n", INT_MIN > 0u); printf("%d\n", INT_MIN > zero); return 0; } $ gcc test.c && ./a.out 1 0
Jan 31 2018
On 1/31/18 6:19 PM, Azi Hassan wrote:On Saturday, 27 January 2018 at 14:13:49 UTC, kdevel wrote:No, the answer is, there's a shortcut optimization used by the compiler. See the discussion elsewhere in this thread.I would expect this code enforce3.d --- import std.exception; void main () { int i = int.min; enforce (i > 0); } --- to throw an "Enforcement failed" exception, but it doesn't: $ dmd enforce3.d $ ./enforce3 [nothing]I wonder if it's caused by a comparison between signed and unsigned integers.import std.stdio; void main () { int zero = 0; writeln(int.min > 0u); writeln(int.min > zero); }Note that comparing the literal int.min will get folded into a constant, and do the right thing. You have to assign it a variable to see the incorrect behavior: int i = int.min; writeln(i > 0); -Steve
Jan 31 2018