August 06, 2022
I haven't used iota much at all. But consider this. We all know exactly what integer arithmetic does, as long as it doesn't overflow.

    for (i = start; i < end; i += step)

I've been around the block enough times with floating point arithmetic that I would *not* use iota with it. The reason is simple enough - if each step is an addition, there's an extra rounding error added on with each increment.

    for (d = start; d < end; d += step)

as opposed to:

    for (d = start; d < end; (d = start + step * i, ++i))

which doesn't have roundoff error accumulation, and might even be faster. How would I know which one iota uses?

I suggest seriously considering (for std2) having iota only work with integers. Floating point stuff can be done as a wrapper around iota.

But the core iota will be simple and understandable.

August 06, 2022
On Sat, Aug 06, 2022 at 09:35:08AM -0700, Walter Bright via Digitalmars-d wrote:
> I haven't used iota much at all. But consider this. We all know exactly what integer arithmetic does, as long as it doesn't overflow.
> 
>     for (i = start; i < end; i += step)
> 
> I've been around the block enough times with floating point arithmetic that I would *not* use iota with it. The reason is simple enough - if each step is an addition, there's an extra rounding error added on with each increment.
> 
>     for (d = start; d < end; d += step)
> 
> as opposed to:
> 
>     for (d = start; d < end; (d = start + step * i, ++i))
> 
> which doesn't have roundoff error accumulation, and might even be faster.  How would I know which one iota uses?
> 
> I suggest seriously considering (for std2) having iota only work with integers. Floating point stuff can be done as a wrapper around iota.
> 
> But the core iota will be simple and understandable.

+1, iota with floating point was iffy to begin with.  Let's take it off the table, start from a clean slate with integers only, and provide the facilities for grafting everything else on using separate facilities.


T

-- 
What do you call optometrist jokes? Vitreous humor.
August 06, 2022
On Saturday, 6 August 2022 at 15:52:13 UTC, H. S. Teoh wrote:
> Here's a first stab at it. Let T be the incoming type (I'm leaning against separately parametrizing the start/end/increment types, I think that's just needlessly complex). Then:
>
> [...]

I think there's a case to be made for allowing a separate increment type, so that you can have e.g. pointers as endpoints with a ptrdiff_t increment. But it's not super essential.

There's also the single-argument iota(n) case to consider. If we're going to go all-in on structural typing, we need a convention for obtaining the "zero" value of a generic type T. The obvious choice is T.init, but that won't work for floating-point types, so maybe `cast(T) 0` is better?
August 08, 2022
On Sat, Aug 06, 2022 at 05:40:54PM +0000, Paul Backus via Digitalmars-d wrote:
> On Saturday, 6 August 2022 at 15:52:13 UTC, H. S. Teoh wrote:
> > Here's a first stab at it. Let T be the incoming type (I'm leaning against separately parametrizing the start/end/increment types, I think that's just needlessly complex). Then:
> > 
> > [...]
> 
> I think there's a case to be made for allowing a separate increment type, so that you can have e.g. pointers as endpoints with a ptrdiff_t increment. But it's not super essential.
> 
> There's also the single-argument iota(n) case to consider. If we're going to go all-in on structural typing, we need a convention for obtaining the "zero" value of a generic type T. The obvious choice is T.init, but that won't work for floating-point types, so maybe `cast(T) 0` is better?

Even the more reason to consider floating-point separately. As Walter said, the core should be integer-only. Floating-point support should be handled separately.

I'm inclined to leave out floating-point entirely, actually. For example, if the user asks for iota(x,y) where x, y are floats and x and y are so large that x++ < nextUp(x), then how many elements should the resulting range have?  If we say, execute ++ each time, then we may have an infinite range. But if we say use z = x + i*y, then it will be a finite range, but will generate the same floating-point values multiple times (because the difference between successive values is too small to be represented, so `x + i*y` will generate the same value for multiple values of i).  Which behaviour is wanted by the user?  No one but the user can say.  Instead of trying to support all of these corner cases, we should just leave it to the user to use .generate with his formula of choice to produce a range with the desired properties, instead of overloading .iota with arbitrary decisions that add significant complexity to the code but may or may not be what the user even wants in the first place.


T

-- 
2+2=4. 2*2=4. 2^2=4. Therefore, +, *, and ^ are the same operation.
August 10, 2022

On Saturday, 6 August 2022 at 15:11:28 UTC, Paul Backus wrote:

>

...because nobody ever bothered to add a (start, end, step) overload for custom types.

We did it 😀

import //sdb.container,
       std.bigint,
       std.stdio;
void main()
{
    auto f = BigInt(ulong.max);
    auto s = BigInt(10);
    auto l = f + 10;
    //iota(f, l, s).writeln;  // compile error
    iras(f, l, s).writeln;  // no problem :)
// [18446744073709551615, 18446744073709551625]
}

alias ir = inclusiveRange;
alias iras = inclusiveRange;
void inclusiveRange(T)(T Args...) if(is(T == bool)) {
  static assert(0, "\nBoolean type cannot be used!");
}
auto inclusiveRange(T = int)
(T f = T(0), T l = T(0), T s = T(1))
if(!is(T == bool)) {
  if(!l) {
    l = f;
    f = 0;
  }
  return InclusiveRange!T(f, l, s);
} /* Error:
   * operation not allowed on `bool` `this.last -= this.step`
   */

struct InclusiveRange(T)
{
  T front, last, step, term;
//...

The continuation of the code:

https://forum.dlang.org/post/nffqokazngtknxetiinu@forum.dlang.org

SDB@79

August 10, 2022
On Saturday, 6 August 2022 at 16:35:08 UTC, Walter Bright wrote:
> I've been around the block enough times with floating point arithmetic that I would *not* use iota with it. The reason is

Something being bad style shouldnt be grounds to break it

If iota was sanely implemented as `countby(T start, T end, S step)` and I used floats; some floating error should be a "wontfix" not something you engineer around and increase the complexity.
1 2 3
Next ›   Last »