digitalmars.D.learn - abs and minimum values
- kyle (13/13) Oct 28 2021 ```
- kyle (5/18) Oct 28 2021 Okay I checked the phobos docs and it does say "Limitations
- Dom DiSc (13/17) Oct 29 2021 I recommend to implement your own abs function this way (was not
- =?UTF-8?Q?Ali_=c3=87ehreli?= (20/23) Oct 29 2021 Combined with automatic type conversions we got from C, it can cause
- Dom DiSc (14/25) Oct 30 2021 This should be no surprise. You need to know what the resulting
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (10/13) Oct 31 2021 In C++ it is undefined behaviour to take the absolute value of a
- Siarhei Siamashka (8/25) Oct 31 2021 Then let's change the example to:
- Imperatorn (3/18) Oct 31 2021 What I would like is for it to mirror math.
- Elronnd (2/3) Oct 31 2021 Use bigints.
- =?UTF-8?Q?Ali_=c3=87ehreli?= (9/25) Oct 31 2021 Yes, the programmer knew all that when they wrote that code when abs()=2...
- Imperatorn (3/21) Oct 31 2021 Just fix it for floats and it would be all round 100 times better
- Imperatorn (4/17) Oct 29 2021 Depends on how you view it. Imo abs should never be able to
- Dom DiSc (7/8) Oct 29 2021 Yes, but phobos defines it to return a signed type, so
- Imperatorn (3/11) Oct 29 2021 Eeh... Why does it return unsigned in phobos? Is it just to stay
- Kagamin (2/2) Oct 29 2021 Unsigned integers aren't numbers.
- Imperatorn (3/5) Oct 29 2021 That's what I mean. The mapping between number classes and data
- Bruce Carneal (5/7) Oct 29 2021 Unsigneds approximate whole numbers of course (truncated on one
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (4/8) Oct 29 2021 D has defined signed integers to be modular, so it represent
- Salih Dincer (7/20) Oct 29 2021 this should work on all types:
- kyle (3/25) Oct 29 2021 Surprisingly, no.
- Siarhei Siamashka (10/23) Oct 31 2021 I think that the best way to deal with this stuff without losing
``` void main() { import std.math : abs, sgn; alias n_type = short; //or int, long, byte, whatever assert(n_type.min == abs(n_type.min)); assert(sgn(abs(n_type.min)) == -1); } ``` I stumbled into this fun today. I understand why abs yields a negative value here with overflow and no promotion. I just want to know if it should. Should abs ever return a negative number? Thanks.
Oct 28 2021
On Thursday, 28 October 2021 at 21:23:15 UTC, kyle wrote:``` void main() { import std.math : abs, sgn; alias n_type = short; //or int, long, byte, whatever assert(n_type.min == abs(n_type.min)); assert(sgn(abs(n_type.min)) == -1); } ``` I stumbled into this fun today. I understand why abs yields a negative value here with overflow and no promotion. I just want to know if it should. Should abs ever return a negative number? Thanks.Okay I checked the phobos docs and it does say "Limitations Does not work correctly for signed intergal types and value Num.min." Should have looked there first, I know. Still seems pretty silly.
Oct 28 2021
On Thursday, 28 October 2021 at 21:26:04 UTC, kyle wrote:Okay I checked the phobos docs and it does say "Limitations Does not work correctly for signed intergal types and value Num.min." Should have looked there first, I know. Still seems pretty silly.I recommend to implement your own abs function this way (was not accepted for phobos, as it now does NOT return the same type as the argument, which was considered a "breaking change" :-( ): ```D /// get the absolute value of x as unsigned type. always succeeds, even for T.min Unsigned!T abs(T)(const(T) x) if(isIntegral!T) { static if(isSigned!T) if(x < 0) return cast(Unsigned!T)-x; return x; } ```
Oct 29 2021
On 10/29/21 1:05 AM, Dom DiSc wrote:I recommend to implement your own abs function this way (was not accepted for phobos, as it now does NOT return the same type as the argument, which was considered a "breaking change" :-( ):Combined with automatic type conversions we got from C, it can cause unexpected results as well. One might expect the following program to print -1 when that definition of abs() is used in an expression: import std.traits; import std.stdio; /// get the absolute value of x as unsigned type. always succeeds, even for T.min Unsigned!T abs(T)(const(T) x) if(isIntegral!T) { static if(isSigned!T) if(x < 0) return cast(Unsigned!T)-x; return x; } void main() { int a = -5; int b = -4; writeln(a + abs(b)); // -5 + 4 == -1? (No!) } The program prints uint.max. Ali
Oct 29 2021
On Friday, 29 October 2021 at 14:20:09 UTC, Ali Çehreli wrote:Unsigned!T abs(T)(const(T) x) if(isIntegral!T) { static if(isSigned!T) if(x < 0) return cast(Unsigned!T)-x; return x; } void main() { int a = -5; int b = -4; writeln(a + abs(b)); // -5 + 4 == -1? (No!) } The program prints uint.max.This should be no surprise. You need to know what the resulting type of int + uint should be. And it is ...... uint! which is one of the stupit integer-promotion rules inherited from C. I just don't understand how "promoting" something by dropping some important information (the sign) and on the fly also destroying the absolute value can ever be a good choice. I always thought it should be the other way round. The way it is is like "promoting" int + float to int (by discarding the fraction part and possibly too high exponents). And those two types are also of same size, so this is not an argument. Promotion should always be in a direction where it at least sometimes can be correct.
Oct 30 2021
On Sunday, 31 October 2021 at 05:04:33 UTC, Dom DiSc wrote:This should be no surprise. You need to know what the resulting type of int + uint should be. And it is ...... uint! which is one of the stupit integer-promotion rules inherited from C.In C++ it is undefined behaviour to take the absolute value of a value that has no positive representation. I assume the same is true for C? So you can write a compiler that detects it and fails. You cannot do this in D as int is defined to represent an infinite set of numbers (mapped as a circle). So in D, you could say that the abs of the most negative value is a positive value that is represented as a negative due to circular wrapping. If this happens in C then it is a bug. If it happens in D, then it is a defined feature of the language.
Oct 31 2021
On Sunday, 31 October 2021 at 05:04:33 UTC, Dom DiSc wrote:On Friday, 29 October 2021 at 14:20:09 UTC, Ali Çehreli wrote:Then let's change the example to: int b = -4; writeln(-abs(b)); What would one normally expect to be printed here? Should the unary minus operator also do some kind of implicit "unsigned -> signed" type change magic to accommodate this modified version of the abs function and make it behave in a non-surprising way?Unsigned!T abs(T)(const(T) x) if(isIntegral!T) { static if(isSigned!T) if(x < 0) return cast(Unsigned!T)-x; return x; } void main() { int a = -5; int b = -4; writeln(a + abs(b)); // -5 + 4 == -1? (No!) } The program prints uint.max.This should be no surprise. You need to know what the resulting type of int + uint should be. And it is ...... uint! which is one of the stupit integer-promotion rules inherited from C.
Oct 31 2021
On Sunday, 31 October 2021 at 10:12:49 UTC, Siarhei Siamashka wrote:On Sunday, 31 October 2021 at 05:04:33 UTC, Dom DiSc wrote:What I would like is for it to mirror math.On Friday, 29 October 2021 at 14:20:09 UTC, Ali Çehreli wrote:Then let's change the example to: int b = -4; writeln(-abs(b)); What would one normally expect to be printed here? Should the unary minus operator also do some kind of implicit "unsigned -> signed" type change magic to accommodate this modified version of the abs function and make it behave in a non-surprising way?[...]This should be no surprise. You need to know what the resulting type of int + uint should be. And it is ...... uint! which is one of the stupit integer-promotion rules inherited from C.
Oct 31 2021
On Sunday, 31 October 2021 at 10:32:50 UTC, Imperatorn wrote:What I would like is for it to mirror math.Use bigints.
Oct 31 2021
On 10/30/21 10:04 PM, Dom DiSc wrote:On Friday, 29 October 2021 at 14:20:09 UTC, Ali =C3=87ehreli wrote:fUnsigned!T abs(T)(const(T) x) if(isIntegral!T) { static if(isSigned!T) if(x < 0) return cast(Unsigned!T)-x; return x; } void main() { int a =3D -5; int b =3D -4; writeln(a + abs(b)); // -5 + 4 =3D=3D -1? (No!) } The program prints uint.max.This should be no surprise. You need to know what the resulting type o=int + uint should be.Yes, the programmer knew all that when they wrote that code when abs()=20 behaved the way it behaves today. The code is an example of why abs() cannot be changed today to what you=20 propose. It would be a bad kind of breaking change: No compilation=20 error, no run-time error (if unlucky), and a weird bug 42 months from tod= ay. Ali
Oct 31 2021
On Friday, 29 October 2021 at 08:05:35 UTC, Dom DiSc wrote:On Thursday, 28 October 2021 at 21:26:04 UTC, kyle wrote:Just fix it for floats and it would be all round 100 times better than what we currently have ☀Okay I checked the phobos docs and it does say "Limitations Does not work correctly for signed intergal types and value Num.min." Should have looked there first, I know. Still seems pretty silly.I recommend to implement your own abs function this way (was not accepted for phobos, as it now does NOT return the same type as the argument, which was considered a "breaking change" :-( ): ```D /// get the absolute value of x as unsigned type. always succeeds, even for T.min Unsigned!T abs(T)(const(T) x) if(isIntegral!T) { static if(isSigned!T) if(x < 0) return cast(Unsigned!T)-x; return x; } ```
Oct 31 2021
On Thursday, 28 October 2021 at 21:23:15 UTC, kyle wrote:``` void main() { import std.math : abs, sgn; alias n_type = short; //or int, long, byte, whatever assert(n_type.min == abs(n_type.min)); assert(sgn(abs(n_type.min)) == -1); } ``` I stumbled into this fun today. I understand why abs yields a negative value here with overflow and no promotion. I just want to know if it should. Should abs ever return a negative number? Thanks.Depends on how you view it. Imo abs should never be able to return a negative value since it should be the distance/length from 0 and alternaticely root of x^2 .
Oct 29 2021
On Friday, 29 October 2021 at 08:33:07 UTC, Imperatorn wrote:Imo abs should never be able to return a negative valueYes, but phobos defines it to return a signed type, so theoretical it can return negative values. And they won't change that. I really think it should return an unsigned type, but that's a "breaking change". Bullshit policy!
Oct 29 2021
On Friday, 29 October 2021 at 09:35:09 UTC, Dom DiSc wrote:On Friday, 29 October 2021 at 08:33:07 UTC, Imperatorn wrote:Eeh... Why does it return unsigned in phobos? Is it just to stay compatible with C? 🤔Imo abs should never be able to return a negative valueYes, but phobos defines it to return a signed type, so theoretical it can return negative values. And they won't change that. I really think it should return an unsigned type, but that's a "breaking change". Bullshit policy!
Oct 29 2021
On Friday, 29 October 2021 at 14:23:49 UTC, Kagamin wrote:Unsigned integers aren't numbers. assert(-abs(1)<0);That's what I mean. The mapping between number classes and data types are too vague.
Oct 29 2021
On Friday, 29 October 2021 at 14:23:49 UTC, Kagamin wrote:Unsigned integers aren't numbers. assert(-abs(1)<0);Unsigneds approximate whole numbers of course (truncated on one side). Likewise signeds approximate integers (across a restricted interval). As always, we need to be careful with approximations.
Oct 29 2021
On Thursday, 28 October 2021 at 21:23:15 UTC, kyle wrote:I stumbled into this fun today. I understand why abs yields a negative value here with overflow and no promotion. I just want to know if it should. Should abs ever return a negative number? Thanks.D has defined signed integers to be modular, so it represent numbers mapped to a circle rather than a line. Is that a good idea? No, but that is what you have.
Oct 29 2021
On Thursday, 28 October 2021 at 21:23:15 UTC, kyle wrote:``` void main() { import std.math : abs, sgn; alias n_type = short; //or int, long, byte, whatever assert(n_type.min == abs(n_type.min)); assert(sgn(abs(n_type.min)) == -1); } ``` I stumbled into this fun today. I understand why abs yields a negative value here with overflow and no promotion. I just want to know if it should. Should abs ever return a negative number? Thanks.this should work on all types: ```d auto sign(T)(T n) { return abs(n) / n; } ```
Oct 29 2021
On Friday, 29 October 2021 at 18:19:58 UTC, Salih Dincer wrote:On Thursday, 28 October 2021 at 21:23:15 UTC, kyle wrote:Surprisingly, no. sign(short.min) == 1``` void main() { import std.math : abs, sgn; alias n_type = short; //or int, long, byte, whatever assert(n_type.min == abs(n_type.min)); assert(sgn(abs(n_type.min)) == -1); } ``` I stumbled into this fun today. I understand why abs yields a negative value here with overflow and no promotion. I just want to know if it should. Should abs ever return a negative number? Thanks.this should work on all types: ```d auto sign(T)(T n) { return abs(n) / n; } ```
Oct 29 2021
On Thursday, 28 October 2021 at 21:23:15 UTC, kyle wrote:``` void main() { import std.math : abs, sgn; alias n_type = short; //or int, long, byte, whatever assert(n_type.min == abs(n_type.min)); assert(sgn(abs(n_type.min)) == -1); } ``` I stumbled into this fun today. I understand why abs yields a negative value here with overflow and no promotion. I just want to know if it should. Should abs ever return a negative number? Thanks.I think that the best way to deal with this stuff without losing sanity is to introduce a special constant "n_type.nan" for signed integer data types. Then you get "abs(n_type.nan) == n_type.nan", which makes more sense. All the uses of "n_type.min" in your code can be replaced either with "n_type.nan" or "-n_type.max", depending on the context and your intention. The existing constant "n_type.min" can be deprecated for signed integer types. Compilers and static analysis tools could warn about its use.
Oct 31 2021