<ol dir="auto">
<li>What version of Go are you using (<code class="notranslate">go version</code>)?<br>
1.6</li>
<li>What operating system and processor architecture are you using (<code class="notranslate">go env</code>)?<br>
linux amd64</li>
<li>What did you do?<br>
<a href="https://play.golang.org/p/1zJjQLBlMK" rel="nofollow">https://play.golang.org/p/1zJjQLBlMK</a></li>
<li>What did you expect to see?<br>
I want <code class="notranslate">float32(1e-45) == float32(math.SmallestNonzeroFloat32)</code> to be true</li>
<li>What did you see instead?<br>
I got <code class="notranslate">float32(1e-45) == 0</code> to be true, which is unexpected.<br>
However <code class="notranslate">float32(1.1e-45) == float32(math.SmallestNonzeroFloat32)</code> is true, as expected.<br>
I don't know whether there are other values that may be affected as well.  I have not tried to find the faulty code.</li>
</ol>
<p dir="auto">I think that <code class="notranslate">fmt.{Print,Scan}</code> and <code class="notranslate">strconv.{Append,Format,Parse}Float</code> are correct; at least my manual testing showed that I could perform roundtrips from <code class="notranslate">math.SmallestNonzeroFloat32</code>, and some other manually-tested values.</p>
<p dir="auto">But I believe that the compiler is doing the wrong thing, since <code class="notranslate">1e-45</code> is closer to <code class="notranslate">math.SmallestNonzeroFloat32</code> than it is to <code class="notranslate">0</code>.  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.<br>
<a href="https://golang.org/ref/spec#Constants" rel="nofollow">https://golang.org/ref/spec#Constants</a><br>
<a href="https://golang.org/ref/spec#Conversions" rel="nofollow">https://golang.org/ref/spec#Conversions</a></p>
<p dir="auto">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 <code class="notranslate">math.SmallestNonzeroFloat32</code>, I end up with <code class="notranslate">float32(1e-45)</code> in my generated code, but that is broken since it is interpreted as <code class="notranslate">float32(0)</code> by the Go compiler.</p>
<p dir="auto">Note that this is a different problem from <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="105901749" data-permission-text="Title is private" data-url="https://github.com/golang/go/issues/12576" data-hovercard-type="issue" data-hovercard-url="/golang/go/issues/12576/hovercard" href="https://github.com/golang/go/issues/12576">#12576</a>, 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.</p>
<p dir="auto">This is a follow-up to issue <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="137069594" data-permission-text="Title is private" data-url="https://github.com/golang/go/issues/14553" data-hovercard-type="issue" data-hovercard-url="/golang/go/issues/14553/hovercard" href="https://github.com/golang/go/issues/14553">#14553</a>. 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 <code class="notranslate">0.5 * 2**-149 (0.1000p-149) &lt; x &lt; 0.75 * 2**-149 (0.1100p-149)</code> for float32 (analogously for float64).</p>
<p dir="auto">Since the compiler is using this code, for these numbers we get the wrong bit patterns when converting/rounding at compile-time (constant evaluation):</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="package main

import (
    &quot;fmt&quot;
    &quot;math&quot;
)

const p149 = 1.0 / (1 &lt;&lt; 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(&quot;%016x  %016x\n&quot;, math.Float32bits(a), math.Float32bits(b))
}"><pre class="notranslate"><code class="notranslate">package main

import (
    "fmt"
    "math"
)

const p149 = 1.0 / (1 &lt;&lt; 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))
}
</code></pre></div>
<p dir="auto">produces</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="0000000000000000  0000000000000000
0000000000000000  0000000000000000
0000000000000000  0000000000000001
0000000000000000  0000000000000001
0000000000000001  0000000000000001"><pre class="notranslate"><code class="notranslate">0000000000000000  0000000000000000
0000000000000000  0000000000000000
0000000000000000  0000000000000001
0000000000000000  0000000000000001
0000000000000001  0000000000000001
</code></pre></div>
<p dir="auto">(the left column is incorrect).</p>
<p dir="auto">The problem in this case seems to be with rounding per se, and not so much the Float32/64 conversions.</p>