Re: Very strange problem with comparing floating point numbers

2012-10-01 Thread Maxim Fomin

On Monday, 1 October 2012 at 21:23:31 UTC, monarch_dodra wrote:
What I was saying is that for built in types such a floats, 
"is" is (should be) no different from "==".


But you catch something interesting: the fact that it provides 
different results is (IMO), a bug. Looking at it, I'd say the 
bug is probably that "==" is overly sensitive to extended 
precision.


I've filed a BR:
http://d.puremagic.com/issues/show_bug.cgi?id=8745

Please feel free to add anything to it. We'll see if Walter 
will react to it for a more definite answer.


It looks like dmd uses x87 comparison instructions which are 
inexact comparing to is. So, similarity of is and == operators on 
built-in and user-defined types may be subject to 
float/double/real exception.


Re: Very strange problem with comparing floating point numbers

2012-10-01 Thread monarch_dodra

On Monday, 1 October 2012 at 13:08:07 UTC, Maxim Fomin wrote:

2012/10/1 monarch_dodra :

On Monday, 1 October 2012 at 11:36:43 UTC, Maxim Fomin wrote:


On Sunday, 30 September 2012 at 17:07:19 UTC, monarch_dodra 
wrote:


As a rule of thumb, NEVER use opEqual with floating point 
types aniways.
You need to use some sort of comparison with leway for 
error, such as

std.math.approxEqual.



It is possible to compare exactly floating point types by 
binary

comparison, if it provides some benefits.

import std.stdio;
import std.math;

@property float getFloat()
{
   return sqrt(1.1);
}

void main()
{
   writeln(getFloat is getFloat);  // doesn't fail
}



I think that what you are comparing here is the functions (the 
address), and

not the results of the call. Try
writeln(getFloat() is getFloat()); //*May* fail



http://dpaste.dzfl.pl/1f94c0b1
[SNIP]


Hum, yes, I guess I was wrong about the comparison of functions. 
Sorry!


Also, "is" works like opEqual on built in types, AFAIK, it 
doesn't use any

"binary" magic or anything like that.


I don't understand what you are trying to say. Is operator at 
runtime
compares two objects without calling opEquals functions (if 
applied on
user-defined types). For built-in and derived types it is 
similar to

== operator. Although, I am suprised that TDPL and spec doesn't
mention it (focused only on CT usage), there is a paragraph
(http://ddili.org/ders/d.en/null_is.html) from Turkish D book 
which
clearly shows such usage - so, I think this a valid D feature. 
Object

comparison at low-level (repz cmpsb) means binary comparison.


What I was saying is that for built in types such a floats, "is" 
is (should be) no different from "==".


But you catch something interesting: the fact that it provides 
different results is (IMO), a bug. Looking at it, I'd say the bug 
is probably that "==" is overly sensitive to extended precision.


I've filed a BR:
http://d.puremagic.com/issues/show_bug.cgi?id=8745

Please feel free to add anything to it. We'll see if Walter will 
react to it for a more definite answer.


Re: Very strange problem with comparing floating point numbers

2012-10-01 Thread Maxim Fomin
2012/10/1 monarch_dodra :
> On Monday, 1 October 2012 at 11:36:43 UTC, Maxim Fomin wrote:
>>
>> On Sunday, 30 September 2012 at 17:07:19 UTC, monarch_dodra wrote:
>>>
>>> As a rule of thumb, NEVER use opEqual with floating point types aniways.
>>> You need to use some sort of comparison with leway for error, such as
>>> std.math.approxEqual.
>>
>>
>> It is possible to compare exactly floating point types by binary
>> comparison, if it provides some benefits.
>>
>> import std.stdio;
>> import std.math;
>>
>> @property float getFloat()
>> {
>>return sqrt(1.1);
>> }
>>
>> void main()
>> {
>>writeln(getFloat is getFloat);  // doesn't fail
>> }
>
>
> I think that what you are comparing here is the functions (the address), and
> not the results of the call. Try
> writeln(getFloat() is getFloat()); //*May* fail
>

http://dpaste.dzfl.pl/1f94c0b1

It works with -m32 too.

 _Dmain:
   0x0806d0e4 <+0>: push   %ebp
   0x0806d0e5 <+1>: mov%esp,%ebp
   0x0806d0e7 <+3>: sub$0x10,%esp
   0x0806d0ea <+6>: push   %esi
   0x0806d0eb <+7>: push   %edi
   0x0806d0ec <+8>: call   0x806d0d4 <_D4test8getFloatFNdZf>
   0x0806d0f1 <+13>:fstps  -0x10(%ebp)
   0x0806d0f4 <+16>:lea-0x10(%ebp),%esi
   0x0806d0f7 <+19>:call   0x806d0d4 <_D4test8getFloatFNdZf>
   0x0806d0fc <+24>:fstps  -0xc(%ebp)
   0x0806d0ff <+27>:lea-0xc(%ebp),%edi
   0x0806d102 <+30>:mov$0x4,%ecx
   0x0806d107 <+35>:xor%eax,%eax
   0x0806d109 <+37>:repz cmpsb %es:(%edi),%ds:(%esi)
   0x0806d10b <+39>:je 0x806d112 <_Dmain+46>
   0x0806d10d <+41>:sbb%eax,%eax
   0x0806d10f <+43>:sbb$0x,%eax
   0x0806d112 <+46>:neg%eax
   0x0806d114 <+48>:sbb%eax,%eax
   0x0806d116 <+50>:inc%eax
   0x0806d117 <+51>:call   0x806d164 
<_D3std5stdio14__T7writelnTbZ7writelnFbZv>
   0x0806d11c <+56>:call   0x806d0d4 <_D4test8getFloatFNdZf>
   0x0806d121 <+61>:fstps  -0x8(%ebp)
   0x0806d124 <+64>:lea-0x8(%ebp),%esi
   0x0806d127 <+67>:call   0x806d0d4 <_D4test8getFloatFNdZf>
   0x0806d12c <+72>:fstps  -0x4(%ebp)
   0x0806d12f <+75>:lea-0x4(%ebp),%edi
   0x0806d132 <+78>:mov$0x4,%ecx
   0x0806d137 <+83>:xor%eax,%eax
   0x0806d139 <+85>:repz cmpsb %es:(%edi),%ds:(%esi)
   0x0806d13b <+87>:je 0x806d142 <_Dmain+94>
   0x0806d13d <+89>:sbb%eax,%eax
   0x0806d13f <+91>:sbb$0x,%eax
   0x0806d142 <+94>:neg%eax
   0x0806d144 <+96>:sbb%eax,%eax
   0x0806d146 <+98>:inc%eax
   0x0806d147 <+99>:call   0x806d164 
<_D3std5stdio14__T7writelnTbZ7writelnFbZv>
   0x0806d14c <+104>:   call   0x806d0d4 <_D4test8getFloatFNdZf>
   0x0806d151 <+109>:   sub$0x4,%esp
   0x0806d154 <+112>:   fstps  (%esp)
   0x0806d157 <+115>:   call   0x806d588
<_D3std5stdio14__T7writelnTfZ7writelnFfZv>
   0x0806d15c <+120>:   xor%eax,%eax
   0x0806d15e <+122>:   pop%edi
   0x0806d15f <+123>:   pop%esi
   0x0806d160 <+124>:   leave
   0x0806d161 <+125>:   ret

> Also, "is" works like opEqual on built in types, AFAIK, it doesn't use any
> "binary" magic or anything like that.

I don't understand what you are trying to say. Is operator at runtime
compares two objects without calling opEquals functions (if applied on
user-defined types). For built-in and derived types it is similar to
== operator. Although, I am suprised that TDPL and spec doesn't
mention it (focused only on CT usage), there is a paragraph
(http://ddili.org/ders/d.en/null_is.html) from Turkish D book which
clearly shows such usage - so, I think this a valid D feature. Object
comparison at low-level (repz cmpsb) means binary comparison.


Re: Very strange problem with comparing floating point numbers

2012-10-01 Thread monarch_dodra

On Monday, 1 October 2012 at 11:36:43 UTC, Maxim Fomin wrote:
On Sunday, 30 September 2012 at 17:07:19 UTC, monarch_dodra 
wrote:
As a rule of thumb, NEVER use opEqual with floating point 
types aniways. You need to use some sort of comparison with 
leway for error, such as std.math.approxEqual.


It is possible to compare exactly floating point types by 
binary comparison, if it provides some benefits.


import std.stdio;
import std.math;

@property float getFloat()
{
   return sqrt(1.1);
}

void main()
{
   writeln(getFloat is getFloat);  // doesn't fail
}


I think that what you are comparing here is the functions (the 
address), and not the results of the call. Try

writeln(getFloat() is getFloat()); //*May* fail

Also, "is" works like opEqual on built in types, AFAIK, it 
doesn't use any "binary" magic or anything like that.


Re: Very strange problem with comparing floating point numbers

2012-10-01 Thread Maxim Fomin

On Sunday, 30 September 2012 at 17:07:19 UTC, monarch_dodra wrote:
As a rule of thumb, NEVER use opEqual with floating point types 
aniways. You need to use some sort of comparison with leway for 
error, such as std.math.approxEqual.


It is possible to compare exactly floating point types by binary 
comparison, if it provides some benefits.


import std.stdio;
import std.math;

@property float getFloat()
{
   return sqrt(1.1);
}

void main()
{
   writeln(getFloat is getFloat);  // doesn't fail
}



Re: Very strange problem with comparing floating point numbers

2012-09-30 Thread jerro

On Monday, 1 October 2012 at 04:10:25 UTC, Ivan Agafonov wrote:

On Sunday, 30 September 2012 at 06:20:56 UTC, jerro wrote:
The second one uses fstp twice, then fld twice. I don't know, 
maybe

this could be a bug.


You're right the lack of one fst/fld in the first case is a 
bug. x87 floating point registers are 80 bit. This:


fstpdword [ebp-0CH]

Converts the value in ST0 to single precision float and stores 
it to memory (and pops ST0). When it is later loaded with fld, 
it is not the same as before storing since some precision is 
lost (because the D code compares floats and not reals, this 
is the correct behavior). In the first example, this storing 
and loading only happens for the first function call. For the 
second call the value is returned in ST0 and stays in x87 
registers until it is compared with fucompp so it is not 
truncated as the result of the first function call was. That's 
why the compared values are not equal.


Can you or anyone report this bug? I don't know how to do this.


I'm not really sure that this is a bug anymore.  Apparently c++ 
does this like this too 
(http://www.parashift.com/c++-faq/floating-point-arith2.html). I 
do think it would be more useful if the result of floating point 
comparison would be defined in a cases like this, but for 
floating point operation in general it makes sense to keep 
intermediate results in registers. So I don't know whether 
introducing a special case for this is worth it.


If you care about this, you should probably make a thread on 
digitalmars.D, where more people will see it and comment on it.


Re: Very strange problem with comparing floating point numbers

2012-09-30 Thread Ivan Agafonov

On Sunday, 30 September 2012 at 06:20:56 UTC, jerro wrote:
The second one uses fstp twice, then fld twice. I don't know, 
maybe

this could be a bug.


You're right the lack of one fst/fld in the first case is a 
bug. x87 floating point registers are 80 bit. This:


fstpdword [ebp-0CH]

Converts the value in ST0 to single precision float and stores 
it to memory (and pops ST0). When it is later loaded with fld, 
it is not the same as before storing since some precision is 
lost (because the D code compares floats and not reals, this is 
the correct behavior). In the first example, this storing and 
loading only happens for the first function call. For the 
second call the value is returned in ST0 and stays in x87 
registers until it is compared with fucompp so it is not 
truncated as the result of the first function call was. That's 
why the compared values are not equal.


Can you or anyone report this bug? I don't know how to do this.


Re: Very strange problem with comparing floating point numbers

2012-09-30 Thread jerro
Technically (AFAIK), IEEE754 does need require reproducibility, 
ergo determinism.


It actually requires more than that:

"Algebraic operations covered by IEEE 754, namely + , - , * , / , 
square root ˆš and Binary <-> Decimal Conversion with rare 
exceptions, must be Correctly Rounded to the precision of the 
operation’s destination unless the programmer has specified 
a rounding other than the default. If it does not Overflow, a 
correctly rounded operation’s error cannot exceed half the 
gap between adjacent floating-point numbers astride the 
operation’s ideal ( unrounded ) result. Half-way cases are 
rounded to Nearest Even, which means that the neighbor with last 
digit 0 is chosen."


I don't know if implementation conform exactly to IEEE 754, but I 
doubt there is any commonly used implementation that isn't 
deterministic.


Re: Very strange problem with comparing floating point numbers

2012-09-30 Thread monarch_dodra

On Sunday, 30 September 2012 at 20:47:41 UTC, jerro wrote:
I don't really agree with that. floating point operations are 
just inexact, regardless of optimizations. That's how they 
work, period.


It is true that they are inexact, but inexact and 
non-deterministic are not the same thing. Floating point 
operations are deterministic.


Technically (AFAIK), IEEE754 does need require reproducibility, 
ergo determinism.


You can open an ER requesting the ability to specify the FP 
behavior want (like rounding behavior), the way vstudio does it.


Re: Very strange problem with comparing floating point numbers

2012-09-30 Thread jerro
I don't really agree with that. floating point operations are 
just inexact, regardless of optimizations. That's how they 
work, period.


It is true that they are inexact, but inexact and 
non-deterministic are not the same thing. Floating point 
operations are deterministic. Doing the same operation twice on 
the same machine with the same rounding mode and parameters will 
give you the same result. This should also be true when you do 
those operations using D, and using == on the two results should 
return true.




Re: Very strange problem with comparing floating point numbers

2012-09-30 Thread Andrej Mitrovic
On 9/30/12, Tommi  wrote:
> On Sunday, 30 September 2012 at 01:48:04 UTC, Andrej Mitrovic
> wrote:
>>
>> Dissasembly:
>> __Dmain:; Function begin, communal
>> enter   12, 0   ; 
>> _ C8, 000C, 00
>> call_D4test8getFloatFNdZf   ; 0004
>> _ E8,
>> (rel)
>> ...
>
> Can I tell DMD to produce the assembly, or what did you do to get
> that?
>

I use objconv (can run on win32 only methinks) on an .obj file on
win32 via a batch file:
objconv -fnasm %~nx1 %~n1_disasm.asm  && %~n1_disasm.asm
http://www.agner.org/optimize/objconv.zip


Re: Very strange problem with comparing floating point numbers

2012-09-30 Thread monarch_dodra

On Sunday, 30 September 2012 at 18:31:17 UTC, so wrote:
On Sunday, 30 September 2012 at 17:07:19 UTC, monarch_dodra 
wrote:
On Sunday, 30 September 2012 at 01:29:24 UTC, Ivan Agafonov 
wrote:

// Tell me about this sutation, may be it is a bug?

[SNIP]
// all of this fails!!!
assert (a.length == a.length); // This is really shocking
assert (a.length == a3.length);
[SNIP]


This is just a fact of life regarding how floating point types 
work. Here is a well documented explanation. It pertains to 
C++, but applies.


http://www.parashift.com/c++-faq/floating-point-arith2.html

As a rule of thumb, NEVER use opEqual with floating point 
types aniways. You need to use some sort of comparison with 
leway for error, such as std.math.approxEqual.


Floating point types are trouble enough without these
optimization failures.
There are many unsolved problems, things like approxEqual are 
far

from answering them. Whatever the justifications they come up
with, "a.len == a.len" failure is IMO unacceptable, an opEqual
like this must not fail.

A suggestion: do what i do and have this in your config files.

alias real evil;


I don't really agree with that. floating point operations are 
just inexact, regardless of optimizations. That's how they work, 
period.


Either you can work with inexact results, and you use them, or 
you can't, and don't. Banks don't use floating point types for 
exactly this reason. You have to know what you are getting into 
before you begin.


The real troubles really only start when you start using floating 
point type, but you expect exact results.




Re: Very strange problem with comparing floating point numbers

2012-09-30 Thread so

On Sunday, 30 September 2012 at 17:07:19 UTC, monarch_dodra wrote:
On Sunday, 30 September 2012 at 01:29:24 UTC, Ivan Agafonov 
wrote:

// Tell me about this sutation, may be it is a bug?

[SNIP]
// all of this fails!!!
assert (a.length == a.length); // This is really shocking
assert (a.length == a3.length);
[SNIP]


This is just a fact of life regarding how floating point types 
work. Here is a well documented explanation. It pertains to 
C++, but applies.


http://www.parashift.com/c++-faq/floating-point-arith2.html

As a rule of thumb, NEVER use opEqual with floating point types 
aniways. You need to use some sort of comparison with leway for 
error, such as std.math.approxEqual.


Floating point types are trouble enough without these
optimization failures.
There are many unsolved problems, things like approxEqual are far
from answering them. Whatever the justifications they come up
with, "a.len == a.len" failure is IMO unacceptable, an opEqual
like this must not fail.

A suggestion: do what i do and have this in your config files.

alias real evil;


Re: Very strange problem with comparing floating point numbers

2012-09-30 Thread monarch_dodra

On Sunday, 30 September 2012 at 01:29:24 UTC, Ivan Agafonov wrote:

// Tell me about this sutation, may be it is a bug?

[SNIP]
// all of this fails!!!
assert (a.length == a.length); // This is really shocking
assert (a.length == a3.length);
[SNIP]


This is just a fact of life regarding how floating point types 
work. Here is a well documented explanation. It pertains to C++, 
but applies.


http://www.parashift.com/c++-faq/floating-point-arith2.html

As a rule of thumb, NEVER use opEqual with floating point types 
aniways. You need to use some sort of comparison with leway for 
error, such as std.math.approxEqual.




Re: Very strange problem with comparing floating point numbers

2012-09-30 Thread Ali Çehreli

On 09/30/2012 04:06 AM, Tommi wrote:
> On Sunday, 30 September 2012 at 01:48:04 UTC, Andrej Mitrovic wrote:
>>
>> Dissasembly:
>> __Dmain:; Function begin, communal
>> enter 12, 0 ;  _ C8, 000C, 00
>> call _D4test8getFloatFNdZf ; 0004 _ E8,
>> (rel)
>> ...
>
> Can I tell DMD to produce the assembly, or what did you do to get that?

For a foo.d, after compiling the program and generating foo.o, the two 
options on Linux that I know of:


1) obj2asm that comes with dmd:

$ obj2asm  foo.o > foo.asm

2) objdump that comes with at least my Linux distribution:

$ objdump -d foo.o > foo.asm

Ali


Re: Very strange problem with comparing floating point numbers

2012-09-30 Thread Tommi
On Sunday, 30 September 2012 at 01:48:04 UTC, Andrej Mitrovic 
wrote:


Dissasembly:
__Dmain:; Function begin, communal
enter   12, 0   ;  
_ C8, 000C, 00
call_D4test8getFloatFNdZf   ; 0004 
_ E8,

(rel)
...


Can I tell DMD to produce the assembly, or what did you do to get 
that?


Re: Very strange problem with comparing floating point numbers

2012-09-29 Thread jerro
The second one uses fstp twice, then fld twice. I don't know, 
maybe

this could be a bug.


You're right the lack of one fst/fld in the first case is a bug. 
x87 floating point registers are 80 bit. This:


fstpdword [ebp-0CH]

Converts the value in ST0 to single precision float and stores it 
to memory (and pops ST0). When it is later loaded with fld, it is 
not the same as before storing since some precision is lost 
(because the D code compares floats and not reals, this is the 
correct behavior). In the first example, this storing and loading 
only happens for the first function call. For the second call the 
value is returned in ST0 and stays in x87 registers until it is 
compared with fucompp so it is not truncated as the result of the 
first function call was. That's why the compared values are not 
equal.




Re: Very strange problem with comparing floating point numbers

2012-09-29 Thread Ali Çehreli

On 09/29/2012 06:48 PM, Andrej Mitrovic wrote:

> The second one uses fstp twice, then fld twice. I don't know, maybe
> this could be a bug. My ASM is weak but I see some difference here..

I saw similar differences but my asm is even weaker. :)

I am writing to confirm that I can reproduce the problem with -m32 on a 
64-bit system. The code works as expected without -m32.


Ali



Very strange problem with comparing floating point numbers

2012-09-29 Thread Ivan Agafonov

// Tell me about this sutation, may be it is a bug?

import std.math;
import std.stdio;

struct Vector(int size)
{
union
{
float[size] array = 0;
struct
{
static if (size == 2) float x, y;
static if (size == 3) float x, y, z;
static if (size == 4) float x, y, z, w;
}
}

@property float lengthsqr()
{
static if (size == 2) return x*x + y*y;
static if (size >= 3) return x*x + y*y + z*z;
}

@property float length() { return sqrt(lengthsqr()); }
	@property float length2() { float tmp = sqrt(lengthsqr()); 
return tmp; }

}

void main()
{
auto a = Vector!4([1, 2, 3, 1]);
auto a3 = Vector!3([1, 2, 3]);
assert (a.lengthsqr == 14);

auto alen = a.length; auto a3len = a3.length;

// all of this prints the same number: 0x1.deea2p+1
writefln("%a, %a", alen, a3len);
writefln("%a, %a", a.length, a3.length);
writefln("%a, %a", a.length2, a3.length2);

// passes
assert (alen == a3len);
assert (a.length2 == a3.length2);
assert (cast(real)a.length == cast(real)a3.length);

// all of this fails!!!
assert (a.length == a.length); // This is really shocking
assert (a.length == a3.length);
}