Possible issue with math.Floor on Linux

192 views
Skip to first unread message

Andrei Avram

unread,
May 4, 2018, 1:35:29 PM5/4/18
to golang-nuts
Hello everyone,

Today I ran into a situation that is strange to me. I ran the following code on two Linux machines (go run floor.go), on two Windows ones, and on Go Playground.

package main

import (
"time"
"math"
)

func main() {
datetime := time.Now().Add(time.Hour * 24 * 7 * 4 * 12 * 3)
seconds := -1 * int(time.Now().Sub(datetime).Seconds())
a := 29030400
b := float64(seconds) / float64(a)

println("input:", b)
println("floor:", math.Floor(b))
}

On Linux the output is:

input: +3.000000e+000
floor: +2.000000e+000

On Windows and Playground:

input: +3.000000e+000
floor: +3.000000e+000

As you can see, on Linux the floor value of float value 3 is rounded down to 2, while on Windows/Playground it's 3.

The code was ran with Go 1.10 and 1.10.2.

The system details of one of the Linux machines, as reported by "go bug":

go version go1.10.2 linux/amd64
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/msd/.cache/go-build"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/msd/go/"
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
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-build304261270=/tmp/go-build -gno-record-gcc-switches"
GOROOT/bin/go version: go version go1.10.2 linux/amd64
GOROOT/bin/go tool compile -V: compile version go1.10.2
uname -sr: Linux 4.13.0-37-generic
Distributor ID: Ubuntu
Description: Ubuntu 16.04.4 LTS
Release: 16.04
Codename: xenial
/lib/x86_64-linux-gnu/libc.so.6: GNU C Library (Ubuntu GLIBC 2.23-0ubuntu10) stable release version 2.23, by Roland McGrath et al.
gdb --version: GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1


Is there something I miss or could this be an issue?

Thanks,
Andrei

Ian Lance Taylor

unread,
May 4, 2018, 1:57:23 PM5/4/18
to Andrei Avram, golang-nuts
The builtin println function already does rounding, so you are
comparing two rounded results. Use fmt.Println instead to see the
real values you are working with.

Ian

speter

unread,
May 4, 2018, 2:07:15 PM5/4/18
to Andrei Avram, golang-nuts
b is slightly less than 3 because there is a bit of time between the two calls to time.Now().

If you substitute this:
        fmt.Printf("input: %20.18f\n", b)

you get something like
input: 2.999999965553350911

HTH
Peter

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Andrei Avram

unread,
May 4, 2018, 3:33:32 PM5/4/18
to golang-nuts
Peter and Ian, you are both wright regarding the printing. Still, on Linux I have different values than on Go Playground.

Regarding the speed, Peter, do you think that on Windows the two calls just run faster every time?
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

speter

unread,
May 4, 2018, 3:57:46 PM5/4/18
to Andrei Avram, golang-nuts
So on Linux it's working as expected. In the playground time is "virtual"; the start time is fixed and it doesn't progress unless you explicitly Sleep -- because you don't Sleep in your program, time.Now() returns the same time on both invocation.

So the only slightly surprising part is that on Windows, time as measured by time.Now() doesn't progress between the two statements. But I don't think it is because Windows is so much faster than Linux. :) I'd guess this is because the way Windows manages time is different from how Linux manages it, and/or the way the time package implemented is different between Windows and Linux. The result of the cross-platform differences seems to be less precision on Windows -- at least in this specific scenario.

Peter
 

To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.

Andrei Avram

unread,
May 4, 2018, 4:26:56 PM5/4/18
to golang-nuts
But I don't think it is because Windows is so much faster than Linux
 
Yeah... :) I was exploring all cases.


 and/or the way the time package implemented is different between Windows and Linux

Could this be considered a possible Go bug and would it worth opening an issue on Github?

speter

unread,
May 4, 2018, 4:42:00 PM5/4/18
to Andrei Avram, golang-nuts
To file a bug (and have it treated seriously) you would need to demonstrate that it is causing problems in a program that actually does something useful, not just a pathological code sample. My expectation would be that once you start doing some real processing, the difference between time.Now() invocations becomes non-zero, that is this "issue" doesn't reproduce with "real" practical programs.


To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.

Andrei Avram

unread,
May 4, 2018, 4:55:56 PM5/4/18
to golang-nuts
This code is extracted from something real. Someone on the team noticed the unit tests (which I wrote on a Linux machine) were failing on their Windows machine.
I'll continue studying this and come back if I find something new.

Thank you both for your time, I appreciate it!

Steven Hartland

unread,
May 4, 2018, 6:49:34 PM5/4/18
to Andrei Avram, golang-nuts
You could be seeing a side effect on fact that windows only ticks every 15ms.

Andrei Avram

unread,
May 5, 2018, 12:54:27 AM5/5/18
to golang-nuts
Yes, this could be it. First I was fooled by println rounding 2.9 to 3, thinking floor is not working properly on Linux.

Thanks!

Andrei Avram

unread,
May 6, 2018, 2:41:29 AM5/6/18
to golang-nuts
Indeed, it was about precision, the issue is with Windows, not with Go. I've used only one time.Now call and the planets have aligned.

package main

import (
"time"
"math"
"fmt"
)

func main() {
now := time.Now()

datetime := now.Add(time.Hour * 24 * 7 * 4 * 12 * 3)
seconds := -1 * int(now.Sub(datetime).Seconds())
a := 29030400
x := float64(seconds)/float64(a)

fmt.Println("input:", x, "floor:", math.Floor(x))
}

Result on both systems: input: 3 floor: 3.

Thank you all! 
Reply all
Reply to author
Forward
0 new messages