April 14, 2010
Don wrote:
> bearophile wrote:
>> So far I've just given a light reading of the code. Notes:
> 
>> - pow(x, 2) and sqrt(y) can be written as x ^^ 2 and y ^^ 0.5 (but you have to import std.math anyway, because of a bug).
> 
> That's not a bug. It's intentional. x ^^ y will probably always require import std.math, if y is a floating point number.

Really?  Why is that?  I find that kind of disappointing, I always believed it to be a temporary solution.

I think the inconsistency with the other operators will make this a major WTF for people new to the language.  Why should a^^b require an explicit import while a*b doesn't?

If the language made it possible to overload operators using free functions, I wouldn't mind if opBinary!"^^"(float, float) was implemented in std.math.  The way it is now, it's a halfway built-in, halfway library feature, and just seems halfway altogether.

-Lars
April 14, 2010
Lars T. Kyllingstad wrote:
> Don wrote:
>> bearophile wrote:
>>> So far I've just given a light reading of the code. Notes:
>>
>>> - pow(x, 2) and sqrt(y) can be written as x ^^ 2 and y ^^ 0.5 (but you have to import std.math anyway, because of a bug).
>>
>> That's not a bug. It's intentional. x ^^ y will probably always require import std.math, if y is a floating point number.
> 
> Really?  Why is that?  I find that kind of disappointing, I always believed it to be a temporary solution.
> 
> I think the inconsistency with the other operators will make this a major WTF for people new to the language.  Why should a^^b require an explicit import while a*b doesn't?

Because pow() for floating point, when implemented properly, is a HUGE function, that ends up dragging almost all of std.math into the executable. And I think it's deceptive to do that silently.
To make it completely built-in, basically all of std.math would need to be moved into druntime. Feel free to try to change my mind, of course.

> If the language made it possible to overload operators using free functions, I wouldn't mind if opBinary!"^^"(float, float) was implemented in std.math.  The way it is now, it's a halfway built-in, halfway library feature, and just seems halfway altogether.

You can expect the integration to become cleaner; still, it's a compromise. It was a big fight to get it into the language at all, and I've tried to minimize the cost of it. ^^ is basically syntax sugar, and the price you pay for the additional tiny bit of syntax sugar (avoiding import std.math) has an appalling cost-benefit ratio.

Raising to a float power is really a niche feature. Are there really many uses for ^^ of floats, where std.math isn't imported already (for example, where you don't even use abs()) ?
April 14, 2010
Don:

> That's not a bug. It's intentional. x ^^ y will probably always require import std.math, if y is a floating point number.

What's the rationale behind this decision?
I have filed it as a bug many days ago, because I think it's a wrong intention:
http://d.puremagic.com/issues/show_bug.cgi?id=3832
Having to import all names from the math module to use a built-in operator is unintuitive, not handy, and sounds silly.

Bye,
bearophile
April 14, 2010
Don:

Thank you for your answer and comments.

> Because pow() for floating point, when implemented properly, is a HUGE
> function, that ends up dragging almost all of std.math into the
> executable. And I think it's deceptive to do that silently.
> To make it completely built-in, basically all of std.math would need to
> be moved into druntime.

I have just read the source of pow() and indeed it's a complex function that needs several other functions of math.


>Raising to a float power is really a niche feature.<

Used it as x^^0.5 to perform the square root is not a niche feature, square roots are common enough. And sqrt is an intrinsic, it doesn't need all std.math.

Error messages are not good yet, this program:


import std.c.stdio: printf;
void main() {
    printf("%f\n", 2.5 ^^ 0.5);
}


Generates:

test.d(3): Error: must import std.math to use ^^ operator
test.d(3): Error: undefined identifier module test5.std
test.d(3): Error: no property 'math' for type 'void'
Error: no property 'sqrt' for type 'int'
test.d(3): Error: function expected before (), not __error of type int


Alternative single error:

test.d(4): Error: must import std.math to use "x ^^ floating point".


If I change the code a little the situation doesn't improve a lot:

import std.c.stdio: printf;
import std.math: sqrt;
void main() {
    printf("%f\n", 2.5 ^^ 0.5);
}


test5.d(4): Error: undefined identifier module test5.std
test5.d(4): Error: no property 'math' for type 'void'
Error: no property 'sqrt' for type 'int'
test5.d(4): Error: function expected before (), not __error of type int


So I can see various solutions:

1) Keep the situation as now, but improve the error messages (essentially printing only one error message that's more focused).

2) As the point (1), but in the case of x^^0.5 doesn't require the import math; x^^FP requires the import still.

3) Automatically import the pow() in all modules, even if this causes the import of lot of std.math. So no errors are generated.

4) Automatically import the pow() in a module only if it contains a x^^FP where FP is a floating point != 0.5  So the code in most modules is not affected by std.math.

Regardless what the final decision will be, this topic must be discussed in the D newsgroup, it's not something you have to decide for yourself (despite you are a very programmer, Don). This subthread can even be moved to the main D newsgroup.

Bye,
bearophile
April 14, 2010
bearophile wrote:
> Don:
> 
> Thank you for your answer and comments.
> 
>> Because pow() for floating point, when implemented properly, is a HUGE
>> function, that ends up dragging almost all of std.math into the
>> executable. And I think it's deceptive to do that silently.
>> To make it completely built-in, basically all of std.math would need to
>> be moved into druntime.
> 
> I have just read the source of pow() and indeed it's a complex function that needs several other functions of math.

It's not even implemented correctly yet. A correct implementation is AT LEAST ten times larger.
> 
> 
>> Raising to a float power is really a niche feature.<
> 
> Used it as x^^0.5 to perform the square root is not a niche feature, square roots are common enough.
> And sqrt is an intrinsic, it doesn't need all std.math.


> 
> Error messages are not good yet, this program:
> 
> 
> import std.c.stdio: printf;
> void main() {
>     printf("%f\n", 2.5 ^^ 0.5);
> }
> 
> 
> Generates:
> 
> test.d(3): Error: must import std.math to use ^^ operator
> test.d(3): Error: undefined identifier module test5.std
> test.d(3): Error: no property 'math' for type 'void'
> Error: no property 'sqrt' for type 'int'
> test.d(3): Error: function expected before (), not __error of type int
> 
> 
> Alternative single error:
> 
> test.d(4): Error: must import std.math to use "x ^^ floating point".

Please don't complain about this obvious stuff. Parasitic error suppression is an ongoing task in the compiler. It's gradually improving, and there's nothing special about this particular case.


> If I change the code a little the situation doesn't improve a lot:
> 
> import std.c.stdio: printf;
> import std.math: sqrt;
> void main() {
>     printf("%f\n", 2.5 ^^ 0.5);
> }
> 
> 
> test5.d(4): Error: undefined identifier module test5.std
> test5.d(4): Error: no property 'math' for type 'void'
> Error: no property 'sqrt' for type 'int'
> test5.d(4): Error: function expected before (), not __error of type int
> 
> 
> So I can see various solutions:
> 
> 1) Keep the situation as now, but improve the error messages (essentially printing only one error message that's more focused).
> 
> 2) As the point (1), but in the case of x^^0.5 doesn't require the import math; x^^FP requires the import still.
> 
> 3) Automatically import the pow() in all modules, even if this causes the import of lot of std.math. So no errors are generated.
> 
> 4) Automatically import the pow() in a module only if it contains a x^^FP where FP is a floating point != 0.5  So the code in most modules is not affected by std.math.
> 
> Regardless what the final decision will be, this topic must be discussed in the D newsgroup, it's not something you have to decide for yourself (despite you are a very programmer, Don). This subthread can even be moved to the main D newsgroup.

I see it as a VERY minor issue, we don't need to deal with it for a long time.
April 14, 2010
Don wrote:
> Lars T. Kyllingstad wrote:
>> Don wrote:
>>> bearophile wrote:
>>>> So far I've just given a light reading of the code. Notes:
>>>
>>>> - pow(x, 2) and sqrt(y) can be written as x ^^ 2 and y ^^ 0.5 (but you have to import std.math anyway, because of a bug).
>>>
>>> That's not a bug. It's intentional. x ^^ y will probably always require import std.math, if y is a floating point number.
>>
>> Really?  Why is that?  I find that kind of disappointing, I always believed it to be a temporary solution.
>>
>> I think the inconsistency with the other operators will make this a major WTF for people new to the language.  Why should a^^b require an explicit import while a*b doesn't?
> 
> Because pow() for floating point, when implemented properly, is a HUGE function, that ends up dragging almost all of std.math into the executable. And I think it's deceptive to do that silently.
> To make it completely built-in, basically all of std.math would need to be moved into druntime. Feel free to try to change my mind, of course.

Exponentiation is a built-in operation in FORTRAN, so I made this little program to check:

      program test

      implicit none
      real  :: base, expo, pow

      write (*,*) "Base:"
      read  (*,*) base
      write (*,*) "Exponent:"
      read  (*,*) expo

      pow = base**expo
      write (*,*) pow

      end program test

The produced executable is 11K.  If I replace exponentiation with multiplication, it is still 11K.  Why wouldn't the same be possible in D?

Note that I'm not trying to argue with you, I am sure you know what you're talking about.  I know very little of what's going on under the hood of either compiler, so I'm asking out of curiosity.


>> If the language made it possible to overload operators using free functions, I wouldn't mind if opBinary!"^^"(float, float) was implemented in std.math.  The way it is now, it's a halfway built-in, halfway library feature, and just seems halfway altogether.
> 
> You can expect the integration to become cleaner; still, it's a compromise. It was a big fight to get it into the language at all, and I've tried to minimize the cost of it. ^^ is basically syntax sugar, and the price you pay for the additional tiny bit of syntax sugar (avoiding import std.math) has an appalling cost-benefit ratio.
> 
> Raising to a float power is really a niche feature. Are there really many uses for ^^ of floats, where std.math isn't imported already (for example, where you don't even use abs()) ?

It's the inconsistency with the other operations on built-in types that bothers me, not the hassle of writing 'import std.math'.

But please don't get me wrong, I absolutely prefer the current solution over nothing.  Now that I've gotten used to it I'm never going back. :)

For fun I grepped my numerics library for uses of ^^, and found I've used it >65 times (65 is the line count, and it's used twice in some lines).  And let it be known that the majority of the cases are float^^float! ;)

-Lars
April 14, 2010
Lars T. Kyllingstad wrote:
> Don wrote:
>> Lars T. Kyllingstad wrote:
>>> Don wrote:
>>>> bearophile wrote:
>>>>> So far I've just given a light reading of the code. Notes:
>>>>
>>>>> - pow(x, 2) and sqrt(y) can be written as x ^^ 2 and y ^^ 0.5 (but you have to import std.math anyway, because of a bug).
>>>>
>>>> That's not a bug. It's intentional. x ^^ y will probably always require import std.math, if y is a floating point number.
>>>
>>> Really?  Why is that?  I find that kind of disappointing, I always believed it to be a temporary solution.
>>>
>>> I think the inconsistency with the other operators will make this a major WTF for people new to the language.  Why should a^^b require an explicit import while a*b doesn't?
>>
>> Because pow() for floating point, when implemented properly, is a HUGE function, that ends up dragging almost all of std.math into the executable. And I think it's deceptive to do that silently.
>> To make it completely built-in, basically all of std.math would need to be moved into druntime. Feel free to try to change my mind, of course.
> 
> Exponentiation is a built-in operation in FORTRAN, so I made this little program to check:
> 
>       program test
> 
>       implicit none
>       real  :: base, expo, pow
> 
>       write (*,*) "Base:"
>       read  (*,*) base
>       write (*,*) "Exponent:"
>       read  (*,*) expo
> 
>       pow = base**expo
>       write (*,*) pow
> 
>       end program test
> 
> The produced executable is 11K.  If I replace exponentiation with multiplication, it is still 11K.  Why wouldn't the same be possible in D?

Scratch that, I just remembered that libgfortran is dynamically linked in by default.  However, compiling with -static-libgfortran makes the executable 155K with both exponentiation and multiplication.

-Lars
April 14, 2010
> - the two nested loops in the main are more efficient as ref double, this is something dmd will need to fix;

A test shows that on ldc the two nested loops are a little faster without the ref. I'd like the compiler to use a "const ref" with the foreach iterates on array items bigger than a word.

I've done a related small test:


version (Tango)
    import tango.stdc.stdio: printf;
else
    import std.stdio: printf;

void main() {
    auto data = new double[1_000];
    double tot = 0.0;
    foreach (uint i, ref el; data)
        tot += el * i;
    printf("%f\n", tot);
}


Just the asm of the loop.
Note how ldc keeps two iteration indexes, one in xmm0 and one in ESI, because it's faster this way. LCPI1_0 is .quad 4607182418800017408 that is the double value 1.0.

(Here the presence of ref doesn't change the code produced by ldc, but in the two loops of the original code it makes a little difference).

ldc -O5 -release -inline -enable-unsafe-fp-math -output-s test.d dmd -O -release -inline test.d

-------------------------

ldc, with ref:

.LBB1_1:
    movapd  %xmm0, %xmm1
    mulsd   (%eax,%esi,8), %xmm1
    movsd   16(%esp), %xmm2
    addsd   %xmm1, %xmm2
    movsd   %xmm2, 16(%esp)
    incl    %esi
    cmpl    $1000, %esi
    addsd   .LCPI1_0, %xmm0
    jne .LBB1_1

-------------------------

ldc, without ref:

.LBB1_1:
    movapd  %xmm0, %xmm1
    mulsd   (%eax,%esi,8), %xmm1
    movsd   16(%esp), %xmm2
    addsd   %xmm1, %xmm2
    movsd   %xmm2, 16(%esp)
    incl    %esi
    cmpl    $1000, %esi
    addsd   .LCPI1_0, %xmm0
    jne .LBB1_1

-------------------------

dmd, with ref:

L45:    mov 8[ESP],EDX
        xor ESI,ESI
        fld qword ptr [EDX*8][ECX]
        mov 0Ch[ESP],ESI
        mov EBX,EDX
        inc EDX
        fild    long64 ptr 8[ESP]
        fmulp   ST(1),ST
        fadd    qword ptr 018h[ESP]
        cmp EDX,010h[ESP]
        fstp    qword ptr 018h[ESP]
        jb  L45

-------------------------

dmd, without ref:

L45:    mov 8[ESP],EDX
        xor ESI,ESI
        mov EBX,EDX
        mov 0Ch[ESP],ESI
        fild    long64 ptr 8[ESP]
        fmul    qword ptr [EDX*8][ECX]
        inc EDX
        cmp EDX,010h[ESP]
        fadd    qword ptr 018h[ESP]
        fstp    qword ptr 018h[ESP]
        jb  L45

Bye,
bearophile
April 14, 2010
Lars T. Kyllingstad wrote:
> Lars T. Kyllingstad wrote:
>> Don wrote:
>>> Lars T. Kyllingstad wrote:
>>>> Don wrote:
>>>>> bearophile wrote:
>>>>>> So far I've just given a light reading of the code. Notes:
>>>>>
>>>>>> - pow(x, 2) and sqrt(y) can be written as x ^^ 2 and y ^^ 0.5 (but you have to import std.math anyway, because of a bug).
>>>>>
>>>>> That's not a bug. It's intentional. x ^^ y will probably always require import std.math, if y is a floating point number.
>>>>
>>>> Really?  Why is that?  I find that kind of disappointing, I always believed it to be a temporary solution.
>>>>
>>>> I think the inconsistency with the other operators will make this a major WTF for people new to the language.  Why should a^^b require an explicit import while a*b doesn't?
>>>
>>> Because pow() for floating point, when implemented properly, is a HUGE function, that ends up dragging almost all of std.math into the executable. And I think it's deceptive to do that silently.
>>> To make it completely built-in, basically all of std.math would need to be moved into druntime. Feel free to try to change my mind, of course.
>>
>> Exponentiation is a built-in operation in FORTRAN, so I made this little program to check:
>>
>>       program test
>>
>>       implicit none
>>       real  :: base, expo, pow
>>
>>       write (*,*) "Base:"
>>       read  (*,*) base
>>       write (*,*) "Exponent:"
>>       read  (*,*) expo
>>
>>       pow = base**expo
>>       write (*,*) pow
>>
>>       end program test
>>
>> The produced executable is 11K.  If I replace exponentiation with multiplication, it is still 11K.  Why wouldn't the same be possible in D?
> 
> Scratch that, I just remembered that libgfortran is dynamically linked in by default.  However, compiling with -static-libgfortran makes the executable 155K with both exponentiation and multiplication.
> 
> -Lars

I have a vague recollection that correctly-rounded pow() will require bigint (can't quite remember, though). I'm also concerned about build tools -- I don't want them to have to know about the dependency.
As a bare minimum, the error message will need to improve (with some explanation of why std.math is required, to reduce the WTF factor).
But in any case, it's a very minor issue.
Personally, I'm finding that having ^^ as standard nomenclature is extremely useful, even apart from the use in code.
April 14, 2010
I'm catching up with the posts.

Joseph Wakeling:

> As for iteration, I don't know to what degree D's foreach() across
> arrays compares to for() commands in C++ -- I guess it should be pretty
> close in performance, no?

As you have seen from my benchmarks, when with dmd you use:
foreach (x; arr)
and x is longer than a word, then the loop is not as fast as it can be. When x is longer than a word it's often more efficient to use a ref, especially in the inner loop:
foreach (ref const(double) x; arr)
With the ldc compiler the different between using or not using a ref is zero, or some times using ref slows down the foreach a little.
I have not filed enhancement requests about this because I think it's useless.

Bye,
bearophile