(1) According to the docs == and != are not supported for arrays or
structs. Could someone tell me the official rationale for this
please?
(2) In general comparing floats using == or != isn't considered to be
reliable, so the only real use is to check a denominator for
equality with 0.0 before doing division to avoid division by zero.
There are of course reasonable algorithms for doing float
comparisons. So maybe Go should either:
(a) disallow == and != for floats but provide a global equal()
function or math.EqualFloat64() function, or
(b) keep == and != and make them use an algorithm that does a
smart but fuzzy comparison?
Thanks!
PS OT Quite a while ago "jessta" suggested:
m[key] = _, false
and Rob said
m[key] = _
would be possible but the discussion faded out.
I really hope one of these is taken up; it just seems so
counter-intuitive to supply an actual value simply to have it thrown
away.
--
Mark Summerfield, Qtrac Ltd, www.qtrac.eu
C++, Python, Qt, PyQt - training and consultancy
"Programming in Python 3" - ISBN 0321680561
http://www.qtrac.eu/py3book.html
there are other uses too. for instance, you might use an out-of-range
sentinel value and compare it for equality. also, if you've converted a
float64 directly from an int32, it's non-lossy, so equality
comparisons will work.
this could be useful, for instance, if you're writing an interpreter
and using a single float64 representation for both floats and ints
(quite a few languages do this).
(1) According to the docs == and != are not supported for arrays or
structs. Could someone tell me the official rationale for this
please?
Same struct type, field-by-field equality.
> Please consider also pointer fields and cyclic
> graphs made of them.
Pointer equality is already defined & does not dereference,
so cyclicity via pointers doesn't matter.
> 2) As an array item type can be also a struct, the question of array
> equality can't be resolved before problem 1)
Two slices are equal if their lengths are equal and the
corresponding available elements are equal.
It may not be perfect, but it's a definition. (Recursion through
interfaces might need thinking about.)
Chris
--
Chris "allusive" Dollin
OK, clearly there are reasonable use cases, so I withdraw my suggestion.
Thanks!
--
Mark Summerfield, Qtrac Ltd, www.qtrac.eu
C++, Python, Qt, PyQt - training and consultancy
"C++ GUI Programming with Qt 4" - ISBN 0132354160
Aside from the problem of deciding the best definition for what make
two structs equal, the other problem is that this hides what might be
an intensive operation(If the struct or array is quite large) inside a
very small bit of code.
It's rare that someone would actually want to compare two structs to
see if they contain the same values(same with arrays).
It's much more likely that the programmer actually wants to check if
these two structs are the same struct(pointer) and made a mistake.
> PS OT Quite a while ago "jessta" suggested:
> m[key] = _, false
I don't believe I was the first to suggest this. But still do like it.
> and Rob said
> m[key] = _
> would be possible but the discussion faded out.
the only issue is, what does:
a:= _ mean?
> I really hope one of these is taken up; it just seems so
> counter-intuitive to supply an actual value simply to have it thrown
> away.
it's rare that people put actual values in to a map, it's more common
to put a pointer. In which case
m[key] = nil, false works fine.
Discouraging people from putting values in maps is probably a good thing.
- jessta
--
=====================
http://jessta.id.au
On 11 February 2011 09:47, Jan Mercl <jan....@nic.cz> wrote:
> On Friday, February 11, 2011 9:08:58 AM UTC+1, Mark wrote:
>>
>> (1) According to the docs == and != are not supported for arrays or
>> structs. Could someone tell me the official rationale for this
>> please?
>
> 1) Let me put it the other way around. How do you propose to define equality
> of two struct instances?Same struct type, field-by-field equality.
> Please consider also pointer fields and cyclic
> graphs made of them.Pointer equality is already defined & does not dereference,
so cyclicity via pointers doesn't matter.
a.equals(b) isn't much bigger than a == b.
> It's rare that someone would actually want to compare two structs to
> see if they contain the same values(same with arrays).
> It's much more likely that the programmer actually wants to check if
> these two structs are the same struct(pointer) and made a mistake.
I disagree, because all the times when I've written an
quality test between two structures, that's what I meant.
(Not to mention the cases I didn't write but were written for
me, eg key comparisons in some kind of map.)
> it's rare that people put actual values in to a map, it's more common
> to put a pointer.
I quite often put "actual values" in the non-key part of a map.
Integers and strings and booleans come immediately to mind.
If one wants a non-primitive type in there, I don't see why one
shouldn't be allowed to. Wisdom is not for the compiler.
> Altogether,
> there's probably no general/universal solution how to define struct equality
> and that's IMO the reason why it is left out of the language/specs. And not
> only in Go but in other languages too, presumably for the same reason.
Some of those other languages instead provide ways for the
programmer to attach an appropriate definition of "equality"
to the == operator, or to the corresponding system function if
that's different. This has always struck me as the appropriate
solution.
Some of those other languages instead provide ways for the
programmer to attach an appropriate definition of "equality"
to the == operator, or to the corresponding system function if
that's different. This has always struck me as the appropriate
solution.
Sounds reasonable, but what about unnamed structs? Should "struct{a
int}{1} ==struct{a int}{1}" be true or false? And comparison between
named and unnamed structs, for example:
type t struct{a int}
...
t{1} == struct{a int}{1} // true or false?
It's rare that someone would actually want to compare two structs to
see if they contain the same values(same with arrays).
It's much more likely that the programmer actually wants to check if
these two structs are the same struct(pointer) and made a mistake.
a.equals(b) is much bigger.
In this case equals() is a method that can't have any assumptions made
about it until it is inspected.
a==b is a simple comparison, it's behaviour is simple and it's
performance characteristics are predictable.
for a==b {//something} would be unexpectedly slow if a and b were
large arrays that were being compared.
loops should look like loops because computability depends on it.
They're missing because we are uneasy about the
performance implications, but not because we are
necessarily against them. The conservative thing
to do is omit them until we're sure, because it is
easier to add than remove.
Arrays and structs are straightforward, if expensive.
The real question is what == on slices would mean.
I think a lot of programmers would expect it to
be "same length, same contents", but an equal number
might expect it to be "same pointer+len+cap".
> (2) In general comparing floats using == or != isn't considered to be
> reliable, so the only real use is to check a denominator for
> equality with 0.0 before doing division to avoid division by zero.
This is a popular meme but not true.
== on floats is entirely deterministic, as are the other operations.
It's true that if you are not doing a precise calculation you should
not be testing for precise equality, but that's dependent on the
context.
People who write true numerical algorithms care deeply about
having == when they want it and know exactly what comparison
to use when they don't. There's no "smart but fuzzy comparison"
that would magically work for all use cases. In any event, this
situation is not unique to Go. It is really about floating point math.
If == were really worthless IEEE 754 would not have defined it,
and it wouldn't be present in other languages either.
> m[k] = _
I updated the title on issue 561.
http://code.google.com/p/go/issues/detail?id=561
Russ
Yes, once.
> a==b is a simple comparison, it's behaviour is simple and it's
> performance characteristics are predictable.
> for a==b {//something} would be unexpectedly slow if a and b were
> large arrays that were being compared.
No, it wouldn't: it would be /expectedly/ slow. If you want to
compare big things for equality, it can take a long time.
> loops should look like loops because computability depends on it.
Um ... what?
I think your argument boils down to "operators should be cheap",
and mine boils down to "operators should be expressive".
Go employs structural typing for unnamed types.
http://golang.org/doc/go_spec.html#Type_identity
This is the criteria for being able to compare two values:
The operands must be comparable; that is, the first operand must be
assignable to the type of the second operand, or vice versa.
Thus both comparisons would work, in the first case because the types
are identical, and thus assignable. In the second case, because one
type is unnamed and the underlying types are identical.
I'd generalize that floating point arithmetic is exact for any small
integers times reasonable powers of two. This isn't such an uncommon
scenario (think integrating from zero to one with a power of two
number of steps.
Also, equality checking is useful when you have a stable algorithm
that you want to converge to machine precision, e.g. if you were
forced to implement sqrt yourself.
> this could be useful, for instance, if you're writing an interpreter
> and using a single float64 representation for both floats and ints
> (quite a few languages do this).
>
--
David Roundy
the only issue is, what does:
a:= _ mean?
Doing both of these would be a mistake:
var x []int
var m map[int]int
// identical
x[0] = _
x[0] = 0
// subtly different
m[0] = _
m[0] = 0
Russ
Go employs structural typing for unnamed types.
http://golang.org/doc/go_spec.html#Type_identity
I just wanted to know the reason, and now I do:-)
Personally, I'd rather have the convenience of having the operator---so
long as the documentation explains the performance implications I then
have the choice whether to use it. But with no operator I have no
choice!
> > (2) In general comparing floats using == or != isn't considered to
> > be reliable, so the only real use is to check a denominator for
> > equality with 0.0 before doing division to avoid division by
> > zero.
>
> This is a popular meme but not true.
> == on floats is entirely deterministic, as are the other operations.
> It's true that if you are not doing a precise calculation you should
> not be testing for precise equality, but that's dependent on the
> context.
>
> People who write true numerical algorithms care deeply about
> having == when they want it and know exactly what comparison
> to use when they don't. There's no "smart but fuzzy comparison"
> that would magically work for all use cases. In any event, this
> situation is not unique to Go. It is really about floating point
> math. If == were really worthless IEEE 754 would not have defined it,
> and it wouldn't be present in other languages either.
[snip]
It is a popular meme because it is true---at least for those programmers
who don't know how IEEE-754 works and who aren't necessarily aware that,
e.g., .1 and .2 can't be represented exactly as floating-point numbers.
(I have a comp. sci background where this is year 1 stuff, but I have
met a *lot* of programmers who come from other fields.) I know it isn't
Go-specific and I agree that the current == is fine for floats---but it
isn't always intuitive:
package main
import "fmt"
func main() {
a := 0.0
b := 0.0
for i := 0; i < 10; i++ {
a += 0.1
if i % 2 == 0 {
b += 0.2
} else {
fmt.Printf("%-5t %g %g\n", a == b, a, b)
}
}
}
Produces:
true 0.2 0.2
true 0.4 0.4
false 0.6 0.6000000000000001
false 0.7999999999999999 0.8
false 0.9999999999999999 1
On my 64-bit Linux box.
Thanks!
--
Mark Summerfield, Qtrac Ltd, www.qtrac.eu
C++, Python, Qt, PyQt - training and consultancy
"Rapid GUI Programming with Python and Qt" - ISBN 0132354187
http://www.qtrac.eu/pyqtbook.html
> package main
> import "fmt"
> func main() {
> a := 0.0
> b := 0.0
> for i := 0; i < 10; i++ {
> a += 0.1
> if i % 2 == 0 {
> b += 0.2
> } else {
> fmt.Printf("%-5t %g %g\n", a == b, a, b)
> }
> }
> }
>
> Produces:
>
> true 0.2 0.2
> true 0.4 0.4
> false 0.6 0.6000000000000001
> false 0.7999999999999999 0.8
> false 0.9999999999999999 1
One nice thing demonstrated by your program is that
we changed the default precision for printing a floating
point number in Go. Printing using fmt or strconv will
default to giving exactly enough digits to uniquely identify
the float64 or float32 argument.
That means, if two numbers print the same using
fmt.Print or fmt.Printf with %g or %v, then they are ==.
Contrapositively, if they are !=, they print differently.
This is a tiny thing but a huge win for understanding
the operations. In C your prints would have said
false 0.6 0.6
false 0.8 0.8
false 1 1
Russ