November 24, 2016
On 24.11.2016 09:55, Kagamin wrote:
> On Wednesday, 23 November 2016 at 21:18:27 UTC, Andrei Alexandrescu wrote:
>> [Details needed]
>>
>> I just took a look https://godbolt.org/g/EMy6X4, it's happening.
>
> That's three instructions instead of one shr eax,1

That would be wrong code. Cast to int after dividing.
November 24, 2016
On Thursday, 24 November 2016 at 08:59:03 UTC, Kagamin wrote:
> On Wednesday, 23 November 2016 at 19:40:47 UTC, Ilya Yaroshenko wrote:
>> Current adaptor should be used in a function scope. (Would be great to have a DIP for that kind of semantic check). An RC adaptor can be added too. First we need to find a real world use case where we need to store a random range outside of a function. -- Ilya
>
> You want to instantiate and seed Mt every time you call a function that may need random numbers?

A function can use a global RNG defined by a user or accepts RNG by reference. Range adaptor stores pointer, not value.
November 24, 2016
On Wednesday, 23 November 2016 at 16:54:44 UTC, Andrei Alexandrescu wrote:
> On 11/23/2016 10:52 AM, Ilya Yaroshenko wrote:
>> I started with Engines as basis. The library will be very different
>> comparing with Phobos and _any_ other RNG libraries in terms of floating
>> point generation quality. All FP generation I have seen are not
>> saturated (amount of possible unique FP values are very small comparing
>> with ideal situation because of IEEE arithmetic). I have not found the
>> idea described by others, so it may be an article in the future.
>
> Is this a design matter, or an implementation matter? Could you implement the superior FP generation on the existing std.random API?

Real uniform `rand` (-2^^exp, +2^^exp) and real UniformVariable [a, b) was added. `rand` dose not use IEEE arithmetic to generate a real random number. --Ilya
November 24, 2016
On 23.11.2016 00:55, Andrei Alexandrescu wrote:
> On 11/22/16 1:31 AM, Ilya Yaroshenko wrote:
>>  - `opCall` API instead of range interface is used (similar to C++)
>
> This seems like a gratuitous departure from common D practice. Random
> number generators are most naturally modeled in D as infinite ranges. --
> Andrei

I would posit that it is not actually natural to model random numbers as ranges. Not every random object is a sequence. Different samples are (ideally) independent, so there is little conceptual motivation to group them together -- it just increases the logistic overhead needed to get at the single samples. I.e., the most natural way to use a PRNG range is as follows:

PRNG range;
auto sample(){
    auto r=range.front;
    range.popFront();
    return r;
}

PRNGs are an example where global state is actually desirable, because in contrast to most other programming tasks, usually less predictability is good.
November 24, 2016
On Thursday, 24 November 2016 at 08:55:18 UTC, Kagamin wrote:
> On Wednesday, 23 November 2016 at 21:18:27 UTC, Andrei Alexandrescu wrote:
>> [Details needed]
>>
>> I just took a look https://godbolt.org/g/EMy6X4, it's happening.
>
> That's three instructions instead of one shr eax,1

That's because of the cast(int), dividing by two is optimised just fine. You can even do this:

int foo(int a) { return a / 2; }
uint foo(uint a) { return a / 2; }
int bar(int a)
{
	if (a > 0) return a / 2;
	else assert(0);
}

and get

int example.foo(int):  // slow, to deal with sign
	mov     eax, edi
	shr     eax, 31
	add     eax, edi
	sar     eax
	ret
uint example.foo(uint):  // fast because no sign
	mov     eax, edi
	shr     eax
	ret
int example.bar(int):  // single shift because sign always 0
	test    edi, edi
	jle     .L8
	mov     eax, edi
	sar     eax
	ret
.L8:
        ud2


This should help explain why the extra/different instructions are necessary for signed:

int foo0(int a)
{
	asm
	{
		naked;
		mov     EAX, EDI;
		shr     EAX, 31;
		add     EAX, EDI;
		sar     EAX, 1;
		ret;
	}
}

int foo1(int a)
{
	asm
	{
		naked;
		mov     EAX, EDI;
		sar     EAX, 1;
		ret;
	}
}

int foo2(int a)
{
	asm
	{
		naked;
		mov     EAX, EDI;
		shr     EAX, 1;
		ret;
	}
}


void main()
{
	import std.stdio;
	foreach(i; [int.min, -1, 0, 1, int.max])
		writeln(foo0(i), ' ', foo1(i), ' ', foo2(i));
}

output:

-1073741824 -1073741824 1073741824
0 -1 2147483647
0 0 0
0 0 0
1073741823 1073741823 1073741823
November 24, 2016
On Thursday, 24 November 2016 at 09:46:16 UTC, John Colvin wrote:
> That's because of the cast(int), dividing by two is optimised just fine.

What about Andrei's example?
November 24, 2016
On Thursday, 24 November 2016 at 10:14:27 UTC, Kagamin wrote:
> On Thursday, 24 November 2016 at 09:46:16 UTC, John Colvin wrote:
>> That's because of the cast(int), dividing by two is optimised just fine.
>
> What about Andrei's example?

I was talking about andrei's example. He has a cast(int) in it before the division.
November 24, 2016
On Thursday, 24 November 2016 at 10:16:11 UTC, John Colvin wrote:
> I was talking about andrei's example. He has a cast(int) in it before the division.

And you think that compilation of cast(int)a.length/2 to 3 instructions is optimized just fine? How is it better that one shift?
November 24, 2016
On Thursday, 24 November 2016 at 10:41:44 UTC, Kagamin wrote:
> On Thursday, 24 November 2016 at 10:16:11 UTC, John Colvin wrote:
>> I was talking about andrei's example. He has a cast(int) in it before the division.
>
> And you think that compilation of cast(int)a.length/2 to 3 instructions is optimized just fine? How is it better that one shift?

Because it's correct. If a.length is larger than int.max then cast(int)a.length will half the time be negative and therefore a simple rightshift would not be equivalent to division by 2.
November 24, 2016
On Thursday, November 24, 2016 09:05:34 Kagamin via Digitalmars-d wrote:
> On Wednesday, 23 November 2016 at 21:33:53 UTC, Jonathan M Davis
>
> wrote:
> > though I think that using the comma operator like that is deprecated now. Adding a helper function such as
> >
> > auto getNext(R)(ref R range)
> >     if(isInputRange!R)
> > {
> >     range.popFront();
> >     return range.front;
> > }
> >
> > would solve that problem.
>
> It's the same behavior and suffers from the same problem of reuse of RNG output.

How so? Because someone might call range.front again without bothering to call popFront? If you're paranoid about that, then it can call popFront again before returning (though that would be wasteful).

My take on it is that if you just call popFront before using the random number generator range, then you don't have to worry about what any other code that used it did.

Regardless, the range API is _way_ more useful in general than something like rand(). And if anyone is trying to avoid ranges with random numbers, I think that they're just making their life harder. Occasionally, it's useful to get just one random number, in which case, having to deal with the range API over rand() is kind of annoying, but in general, it's not a problem, and the wrapper function that I suggested basically gives you rand() from a range of random numbers.

Alternatively, you could just do rndGen().take(1).front, and as long as rndGen() gives you a reference type, it works just fine. Unfortunately, std.random did not use reference types for its ranges. _That_ is the big mistake of std.random and the main item that needs to be fixed. There are some other subtleties (e.g. it's useful to be able to save the state of a random number generating range, but you don't necessarily really want it to be a forward range), but those are minor in comparison to the mistake of std.random using value types rather than reference types for ranges.

- Jonathan M Davis