November 21, 2014
Am Wed, 19 Nov 2014 10:42:30 +0000
schrieb "Kagamin" <spam@here.lot>:

> On Tuesday, 18 November 2014 at 18:23:52 UTC, Marco Leise wrote:
> > Aside from the size factor, I personally prefer unsigned types for countable stuff like array lengths. Mixed arithmetics decay to unsinged anyways and you don't need checks like `assert(idx >= 0)`.
> 
> Failing assert(-1 < arr.length) make little sense though, -1 can't be bigger than a non-negative number.

It makes little sense, right.

-- 
Marco

November 21, 2014
Am Wed, 19 Nov 2014 11:01:12 +0000
schrieb "Kagamin" <spam@here.lot>:

> On Tuesday, 18 November 2014 at 18:23:52 UTC, Marco Leise wrote:
> > Mixed arithmetics decay to unsinged anyways and you don't need checks like `assert(idx >= 0)`.
> 
> What such assert gets you, what bound checking doesn't?

It gets me the same when idx is an index into a D slice.

-- 
Marco

November 21, 2014
On 11/21/2014 12:09 PM, ketmar via Digitalmars-d wrote:
> On Fri, 21 Nov 2014 11:52:29 -0800
> Walter Bright via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
>> So 'auto' has different type rules for expressions than anywhere else in D?
>>
>> Consider:
>>
>>       void foo(T)(T a) { ... }
>>
>>       if (x - 1) foo(x - 1);
>>       if (auto a = x - 1) foo(a);
>>
>> and now foo() is instantiated with a different type?
>>
>> I'm afraid I can't sell that to anyone :-(
> the whole thing of `auto` is to let compiler decide. i won't be
> surprised if `a` becomes 80-bit real or bigint here -- that's up to
> compiler to decide which type will be able to hold the whole range.
> `auto` for me means "i don't care what type you'll choose, just do it
> for me and don't lose any bits." this can be some kind of structure
> with overloaded operators, for example.
>

'auto' doesn't mean "break my code if I refactor out expressions into temporaries".
November 21, 2014
Am Sat, 22 Nov 2014 06:34:11 +1100
schrieb "Daniel Murphy" <yebbliesnospam@gmail.com>:

> "Andrei Alexandrescu"  wrote in message news:m4nn38$1lau$2@digitalmars.com...
> 
> > What is your point? (Honest question.)
> 
> That using signed integers exclusively eliminates one class of bugs, while making another class only marginally more likely.
> 
> > Are you proposing that we make all array lengths signed?
> 
> No, I think that ship has sailed.  But I recommend avoiding unsigned types for general arithmetic.

I think it is more about getting into the right mind set. All
hardware integer types are limited and need overflow checking.

As someone using unsigned types all the time, all I need to keep in mind are two rules:

1) Overflow:

  uint number;
  …
  number = 10 * number + ch - '0';

  It is handled with:
  if (number > uint.max / 10)
  if (number > uint.max - (ch - '0'))

  An underflow practically doesn't happen with unsigned
  arithmetic.

2) Subtraction Order

  Subtract the smaller value from the bigger one.

  a) Commonly one entity is of greater magnitude than the
     other:

     fileSize - offset
     length - idx

  b) If both entities are equal before the Lord I make them
     ordered to make rule 1) hold:

     if (fileSize1 > fileSize2)
     {
         // Do one thing
     }
     else
     {
         // Do the other thing
     }

The length of an array is perfectly represented by a size_t.
My goal is to do the technically correct thing and thereby
make overflow bugs impossible. I.e. With unsigned types in
general and size_t in particular you cannot pass anything that
is prone to underflow/overflow. It is all natural numbers and
any overflows must have happened already before the array got
indexed.
Inside opIndex, the unsigned types simplify the range checks
(by the compiler or explicit) by removing the need to test for
< 0.
At the end of the day I find myself using unsigned types much
more frequently than signed types because I find it easier to
keep them in check and reason about.

-- 
Marco

November 21, 2014
Am Wed, 19 Nov 2014 18:20:24 +0000
schrieb "Marc Schütz" <schuetzm@gmx.net>:

> I'd say length being unsigned is fine. The real mistake is that the difference between two unsigned values isn't signed, which would be the most "correct" behaviour.

Now take my position where I explicitly write code relying on the fact that `bigger - smaller` yields correct results.

uint bigger = uint.max;
uint smaller = 2;
if (bigger > smaller)
{
    auto added = bigger - smaller;
    // Now 'added' is an int with the value -3 !
}
else
{
    auto removed = smaller - bigger;
}

In fact checking which value is larger is the only way to handle the full result range of subtracting two machine integers which is ~2 times larger than what the original type can handle:

T.min - T.max .. T.max - T.min

This is one reason why I'd like to just keep working with the original unsigned type, but split the range around the positive/negative pivot with an if-else.

Implicit conversion of unsigned subtractions to signed values would make the above code unnecessarily hard.

> Let people cast the result if they want wrapping (or better, use a helper function to document the intentiion).

-- 
Marco

November 21, 2014
Am Fri, 21 Nov 2014 11:41:55 -0800
schrieb "H. S. Teoh via Digitalmars-d"
<digitalmars-d@puremagic.com>:

> On Fri, Nov 21, 2014 at 10:57:44AM -0800, Walter Bright via Digitalmars-d wrote:
> > On 11/21/2014 10:05 AM, ketmar via Digitalmars-d wrote:
> > >why do you believe that i'm not aware of overflows and don't checking for that? i'm used to think about overflows and do overflow checking in production code since my Z80 days. and i don't believe that "infrequent bug" is better than "frequent bug". both are equally bad.
> > 
> > 
> > Having coded with 16 bit computers for decades, one gets used to thinking about and dealing with overflows :-)
> 
> I used to write 8-bit assembly code on the 6502 (yeah I'm so dating myself), where overflows and underflows happen ALL THE TIME. :-) In fact, they happen so much, that I learned to embrace modulo arithmetic instead of fearing it. I would take advantage of value wrapping to shave off a few cycles here and a few cycles there -- they do add up, given that the CPU only ran at a meager 1MHz, so every little bit counts.
> 
> Then in the 16-bit days, I wrote a sliding-window buffer using a 64kB buffer where the wraparound of the 16-bit index variable was a feature rather than a bug. :-)  Basically, once it reaches within a certain distance from the end of the window, the next 32kB block of data was paged in from disk into the other half of the buffer, so as long as the operation didn't span more than 32kB each time, you can just increment the 16-bit index without needing to check for the end of the buffer -- it'd automatically wrap around to the beginning where the new 32kB block has been loaded once you go past the end. A truly circular buffer! :-P
> 
> 
> T

I used to be a kid playing a GameBoy game called Mystic Quest (which some in Asia may know it as part of the Final Fantasy franchise). One day I got really greedy and spend days collecting gold. Vast amounts of gold. I learned which enemies drop the most and soon got 10000, 20000, 30000, 40000, 50000, 60000, ... NOOOOOOOOOOO!

That game taught me two things. That Dodos are extinct and not checking for overflows is a painful experience.

-- 
Marco

November 22, 2014
On 11/21/14 8:57 AM, ketmar via Digitalmars-d wrote:
> On Fri, 21 Nov 2014 08:31:13 -0800
> Andrei Alexandrescu via Digitalmars-d <digitalmars-d@puremagic.com>
> wrote:
>
>> Would you agree that that would break a substantial amount of correct D
>> code? -- Andrei
> i don't think that code with possible int wrapping and `auto` is
> correct, so the answer is "no". bad code must be made bad.

I think you misunderstood the question. -- Andrei


November 22, 2014
On 11/21/14 11:34 AM, Daniel Murphy wrote:
> "Andrei Alexandrescu"  wrote in message
> news:m4nn38$1lau$2@digitalmars.com...
>
>> What is your point? (Honest question.)
>
> That using signed integers exclusively eliminates one class of bugs,
> while making another class only marginally more likely.
>
>> Are you proposing that we make all array lengths signed?
>
> No, I think that ship has sailed.  But I recommend avoiding unsigned
> types for general arithmetic.

That's reasonable. Also I agree that the discussion is moot. -- Andrei
November 22, 2014
On 11/21/14 12:22 PM, Marco Leise wrote:
> Am Fri, 21 Nov 2014 11:24:37 +0000
> schrieb "bearophile" <bearophileHUGS@lycos.com>:
>
>> Walter Bright:
>>
>>> I thought everyone hated foreach_reverse!
>>
>> I love it!
>>
>> Bye,
>> bearophile
>
> Hey, it is a bit ugly, but I'd pick
>
> foreach_reverse (i; 0 .. length)
>
> anytime over
>
> import std.range;
> foreach (i; iota(length).retro())

I agree, though "foreach (i; length.iota.retro)" is no slouch either! -- Andrei

November 22, 2014
On 11/21/14 1:55 PM, Marco Leise wrote:
> Am Wed, 19 Nov 2014 18:20:24 +0000
> schrieb "Marc Schütz" <schuetzm@gmx.net>:
>
>> I'd say length being unsigned is fine. The real mistake is that
>> the difference between two unsigned values isn't signed, which
>> would be the most "correct" behaviour.
>
> Now take my position where I explicitly write code relying
> on the fact that `bigger - smaller` yields correct results.
>
> uint bigger = uint.max;
> uint smaller = 2;
> if (bigger > smaller)
> {
>      auto added = bigger - smaller;
>      // Now 'added' is an int with the value -3 !
> }
> else
> {
>      auto removed = smaller - bigger;
> }

Interesting insight. Thanks for the many analytical examples you're giving in this thread. -- Andrei