Issue 1:
What version of Go are you using (go version)?

1.6

What operating system and processor architecture are you using (go env)?

linux amd64

What did you do?

https://play.golang.org/p/1zJjQLBlMK

What did you expect to see?

I want float32(1e-45) == float32(math.SmallestNonzeroFloat32) to be true

What did you see instead?

I got float32(1e-45) == 0 to be true, which is unexpected.

However float32(1.1e-45) == float32(math.SmallestNonzeroFloat32) is true, as expected.

I don't know whether there are other values that may be affected as well. I have not tried to find the faulty code.

I think that fmt.{Print,Scan} and strconv.{Append,Format,Parse}Float are correct; at least my manual testing showed that I could perform roundtrips from math.SmallestNonzeroFloat32, and some other manually-tested values.

But I believe that the compiler is doing the wrong thing, since 1e-45 is closer to math.SmallestNonzeroFloat32 than it is to 0. The spec says that we should "round to the nearest representable constant" and "rounding using IEEE 754 round-to-even rules" in the sections on constants and conversions, respectively.

https://golang.org/ref/spec#Constants

https://golang.org/ref/spec#Conversions

FYI the reason this is affecting me is because I have written a Go code generator, for an IDL language similar to protobufs. My language supports generating Go constants and values. When I generate a constant representing math.SmallestNonzeroFloat32, I end up with float32(1e-45) in my generated code, but that is broken since it is interpreted as float32(0) by the Go compiler.

Note that this is a different problem from #12576, which has examples where very small non-zero floating-point exact constants should be rounded to 0, and the question is whether to pick -0 or 0, and we've (now) picked 0.

Issue 2:
This is a follow-up to issue #14553. In the special case of a math.Float number that is smaller than the smallest denormal, but that should be rounded up to the smallest denormal, rounding up doesn't happen for values x with 0.5 * 2**-149 (0.1000p-149) < x < 0.75 * 2**-149 (0.1100p-149) for float32 (analogously for float64).

Since the compiler is using this code, for these numbers we get the wrong bit patterns when converting/rounding at compile-time (constant evaluation):

package main

import (
"fmt"
"math"
)

const p149 = 1.0 / (1 << 149) // 1p-149

const (
m0000 = 0x0 / 16.0 * p149 // = 0.0000p-149
m1000 = 0x8 / 16.0 * p149 // = 0.1000p-149
m1001 = 0x9 / 16.0 * p149 // = 0.1001p-149
m1011 = 0xb / 16.0 * p149 // = 0.1011p-149
m1100 = 0xc / 16.0 * p149 // = 0.1100p-149
)

func main() {
print(float32(m0000), f32(m0000))
print(float32(m1000), f32(m1000))
print(float32(m1001), f32(m1001))
print(float32(m1011), f32(m1011))
print(float32(m1100), f32(m1100))
}

func f32(x float64) float32 {
return float32(x)
}

func print(a, b float32) {
fmt.Printf("%016x %016x\n", math.Float32bits(a), math.Float32bits(b))
}

produces

0000000000000000 0000000000000000
0000000000000000 0000000000000000
0000000000000000 0000000000000001
0000000000000000 0000000000000001
0000000000000001 0000000000000001

(the left column is incorrect).

The problem in this case seems to be with rounding per se, and not so much the Float32/64 conversions.