Bug 8476 – float comparison operand not truncated from real

Status
NEW
Severity
normal
Priority
P3
Component
dmd
Product
D
Version
D2
Platform
x86
OS
Linux
Creation time
2012-07-30T14:28:26Z
Last change time
2024-12-13T18:00:50Z
Assigned to
No Owner
Creator
Ellery Newcomer
Moved to GitHub: dmd#18456 →

Comments

Comment #0 by ellery-newcomer — 2012-07-30T14:28:26Z
The following code produces different results on a comparison. The problem is in fitnessCompare; it looks like one of the two operands has float precision, while the other has real precision. assembly dump: 08065710 <_D19travelling_salesman4mainFZv14fitnessCompareMFS19travelling_salesman10chromosomeS19travelling_salesman10chromosomeZb>: 8065710: 55 push ebp 8065711: 8b ec mov ebp,esp 8065713: 83 ec 10 sub esp,0x10 8065716: ff 75 14 push DWORD PTR [ebp+0x14] 8065719: ff 75 10 push DWORD PTR [ebp+0x10] 806571c: e8 27 00 00 00 call 8065748 <_D19travelling_salesman7fitnessFNaxS19travelling_salesman10chromosomeZf> 8065721: ff 75 0c push DWORD PTR [ebp+0xc] 8065724: ff 75 08 push DWORD PTR [ebp+0x8] 8065727: d9 5d f0 fstp DWORD PTR [ebp-0x10] 806572a: e8 19 00 00 00 call 8065748 <_D19travelling_salesman7fitnessFNaxS19travelling_salesman10chromosomeZf> 806572f: d9 45 f0 fld DWORD PTR [ebp-0x10] 8065732: d9 c9 fxch st(1) 8065734: de d9 fcompp 8065736: df e0 fnstsw ax 8065738: 9e sahf 8065739: b8 01 00 00 00 mov eax,0x1 806573e: 7a 02 jp 8065742 <_D19travelling_salesman4mainFZv14fitnessCompareMFS19travelling_salesman10chromosomeS19travelling_salesman10chromosomeZb+0x32> 8065740: 72 02 jb 8065744 <_D19travelling_salesman4mainFZv14fitnessCompareMFS19travelling_salesman10chromosomeS19travelling_salesman10chromosomeZb+0x34> 8065742: 31 c0 xor eax,eax 8065744: c9 leave 8065745: c2 10 00 ret 0x10 actual code: import std.stdio; import std.random; import std.array; import std.math; struct city{ int x; int y; } struct chromosome{ city[] dna; } void main(){ bool fitnessCompare(chromosome first,chromosome second){ return fitness(first)>fitness(second); } auto less = &fitnessCompare; auto z = chromosome([city(0, 10), city(25, 25), city(10, 65), city(50, 50), city(75, 30), city(20, 0)]); auto f = fitness(z); writeln("fitness(z) > fitness(z) ?",f > f); writeln("fitness(z) > fitness(z) ?",less(z,z)); } float fitness(const chromosome victim) pure{ const city[] cities=city(0,0) ~ victim.dna ~ city(0,0); //we need to start from home and return to home float travelled=0f; for(int x=0;x<cities.length-1;x++) travelled+=distance(cities[x],cities[x+1]); //writeln(100/travelled); return 100/travelled; } float distance(city from,city to) pure{ return sqrt(cast(float)(pow(to.x-from.x,2) + pow(to.y-from.y,2))); }
Comment #1 by bugzilla — 2012-07-30T14:44:36Z
I'm not sure what the issue is here. Can you point to what you think it should be doing? Also, the compiler is allowed to not truncate reals to floats when doing comparisons, even if it is typed as a float. This is a feature, not a bug. The compiler is always allowed to use a higher precision for intermediate calculations than the source is typed.
Comment #2 by bearophile_hugs — 2012-07-30T15:09:48Z
(In reply to comment #1) > The compiler is always allowed to use a higher precision > for intermediate calculations than the source is typed. A disadvantage of this is loss of floating point reproducibility across compilers, maybe similar to using the "-ffast-math" of GCC.
Comment #3 by ellery-newcomer — 2012-07-30T16:09:48Z
(In reply to comment #1) > I'm not sure what the issue is here. Can you point to what you think it should > be doing? > > Also, the compiler is allowed to not truncate reals to floats when doing > comparisons, even if it is typed as a float. This is a feature, not a bug. The > compiler is always allowed to use a higher precision for intermediate > calculations than the source is typed. my diagnosis is probably wrong, but writeln("fitness(z) > fitness(z) ?",f > f); writeln("fitness(z) > fitness(z) ?",less(z,z)); are printing out different results when they should be printing the same thing. dmd 2.059, 32 bit only.
Comment #4 by bugzilla — 2012-07-30T20:58:21Z
f is of type float. z is of type chromosome. Where are they the same types?
Comment #5 by bugzilla — 2012-07-30T21:00:54Z
(In reply to comment #2) > (In reply to comment #1) > > > The compiler is always allowed to use a higher precision > > for intermediate calculations than the source is typed. > > A disadvantage of this is loss of floating point reproducibility across > compilers, maybe similar to using the "-ffast-math" of GCC. D made the decision early on that: more precision == better and that any program that relied on results being less accurate was a faulty program. Use of float should be for: 1. speed 2. less memory consumption and *never* for reduced precision. float guarantees a *minimum* precision, not a maximum.
Comment #6 by bearophile_hugs — 2012-07-31T02:12:51Z
(In reply to comment #5) > D made the decision early on that: > > more precision == better > > and that any program that relied on results being less accurate was a faulty > program. You are right, of course, and such problems are common: http://www.parashift.com/c++-faq-lite/floating-point-arith2.html On the other hand I remember one of my D programs not being as efficient as a very similar C++ program just because the C++ code used float-based operations instead of double-based ones, despite me typing float every FP variable and tagging with "f" every floating point literal. In some cases std.math return a double even if all it's required is a float, and the useless computation of the extra precision slows down the code compared to the C++ code that uses functions that return only a float precision. I presume the float versions of some functions perform less iterations to compute the smaller number of precision digits, and this makes them faster. And in some way std.math was unable to let me use the faster float version.
Comment #7 by ellery-newcomer — 2012-10-15T17:16:10Z
Alright, let's try this again, except with me being comprehensible this time. There is one thing happening here, and that is float f; ... bool result = f < f; It is happening in two places: main, and main.fitnessCompare. In both places, f has the same value (4.2 or something). In both places, f is the result of the pure function fitness. result should always be false for a non-{nan, inf, other floating point screwball} In main.fitnessCompare, result is true. This is wrong. Running the debugger, it appeared to me that one of the two operands to the floating point compare in main.fitnessCompare had 80 bits of precision, while the other only had 64 (or maybe 64 and 32, I don't remember).
Comment #8 by maxim — 2012-10-16T09:04:45Z
*** Issue 8745 has been marked as a duplicate of this issue. ***
Comment #9 by maxim — 2012-10-16T09:18:02Z
Regardless of whether (when comparing floats with reals) compiler should compare with full or truncated to smaller type precision, current behavior seems to be inconsistent with spec. According to the spec in section "Equality Expressions" floating point types are compared bitwise. This means that if two floats are compared, compiler need not care about extra precision but currently it compares 32 bit with 80 bit (issue is better revealed in 8745). But if this is feature and not a bug, spec should be changed and better explain the issue.
Comment #10 by dlang-bugzilla — 2013-05-28T00:05:10Z
*** Issue 10187 has been marked as a duplicate of this issue. ***
Comment #11 by robert.schadek — 2024-12-13T18:00:50Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/18456 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB