Description
What version of Go are you using (go version
)?
$ go version go version go1.15.4 linux/amd64
Does this issue reproduce with the latest release?
Yes.
What operating system and processor architecture are you using (go env
)?
go env
Output
$ go env GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/tmp/freedesktopCache/go-build" GOENV="/home/nsajko/.config/go/env" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GOMODCACHE="/home/nsajko/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/home/nsajko" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/usr/lib/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/lib/go/pkg/tool/linux_amd64" GCCGO="gccgo" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build930141060=/tmp/go-build -gno-record-gcc-switches"
What did you do?
Some explanations are in the comments: https://play.golang.org/p/vaQBl7C6FNm
What did you expect to see?
Nextafter(x, y) should return the next representable
float64 value after x towards y, because none of the
three special cases apply here, because negative and
positive zeros and 1 and -1 are neither the same value
nor NaN.
-4.941e-324 -0 0 0
-0 -0 0 4.941e-324
What did you see instead?
...
-4.941e-324 -0 -0 4.941e-324
-4.941e-324 0 0 4.941e-324
Other notes
Relevant observation: negative and positive floating point zeros are distinct values in Go, but compare equal with the ==
operator. This is OK, but could be considered as the cause of the bug in the implementation.
Inconsistency with other finite float64 arguments
The inconsistency with the handling of zero arguments causes unexpected behavior when looping using the Nextafter functions.
This is not an actual issue for me, but it seems like it might be for someone who uses Nextafter: assuming a nonzero negative number x
is chosen, start iterating on it like this: x = math.Nextafter32(x, 0)
, with the aim of breaking the loop at zero (a similar situation is observed when starting from a positive value, instead of a negative one). The loop, unexpectedly, will never exit in some cases: https://play.golang.org/p/eWs0pg8SXri
Inconsistency with C
Even though at first I thought that this is a relatively straightforward issue (because of the "next representable float64 value" explicit wording), this actually seems to be a tricky issue if compared with C, and it's even possible you may want to consider this a documentation bug instead of a behavioral bug, along with a couple other options.
The C nextafter functions were supposedly the inspiration for the Go version so it might be relevant to compare to them, also, it might be desirable to be compatible with them if you're appropriating their names already.
Both C17 and the current C draft have this to say about nextafter:
Description
The nextafter functions determine the next representable value, in the type of the function, after x
in the direction of y, where x and y are first converted to the type of the function.244) The nextafter
functions return y if x equals y. A range error may occur if the magnitude of x is the largest finite
value representable in the type and the result is infinite or not representable in the type.
Returns
The nextafter functions return the next representable value in the specified format after x in the
direction of y.
Notice the return y if x equals y
explicit requirement about zeros - this is where C differs from Go currently.
But, except for that, the C implementations are actually like Go: they don't follow the spec when x is negative zero and y is a positive nonzero value, or when x is positive zero and y is a negative nonzero value. (In that case the other zero is "skipped over".)
C program for comparison (give it "0" on its stdin):
#include <math.h>
#include <stdio.h>
int
main(void) {
int n;
scanf("%d", &n);
double x = n;
double negX = -x;
printf("%.4g %.4g %.4g %.4g\n%.4g %.4g %.4g %.4g\n",
nextafter(negX, (double)-1), negX, nextafter(negX, x), nextafter(negX, (double)1),
nextafter(x, (double)-1), nextafter(x, negX), x, nextafter(x, (double)1));
return 0;
}
Outputs:
-4.941e-324 -0 0 4.941e-324
-4.941e-324 -0 0 4.941e-324