【问题标题】:Golang exercise slices how does it deal with big valuesGolang 练习切片如何处理大值
【发布时间】:2016-08-20 02:27:13
【问题描述】:

我正在阅读 Golang 教程,但对于切片练习中的一些值的作用,我有点困惑。 https://tour.golang.org/moretypes/18

这是我感到困惑的代码:

值 0 是一个完美的蓝色像素,值 255 是一个完美的白色像素。所以当显示的值是某种形式的x*y 时,这里会发生什么(我做了/20 以使图像更大一点,更容易看到)。

如果您水平跟随图像,您会看到在过程中的某个时刻,不断增加的 x 和 y 值似乎恢复为蓝色(0 值)如果我在返回中键入像 256 这样的静态值我得到一个编译错误。所以它显然不允许数字 go off the scale 并恢复为 0 或任何东西。那么它是如何得到图中的蓝色曲线的呢?

此处导入源:https://github.com/golang/tour/blob/master/pic/pic.go#L15

package main

import "golang.org/x/tour/pic"

func Pic(dx, dy int) [][]uint8 {
    //First, the array has to be made so we can put some values in it later
    //This only makes the second dimension of the array ([[uint8 dy]])?
    image := make([][]uint8, dy)

    //The inputs into the function are Int's, so it is ok to have a non uint8
    //loop initializer
    for x := 0; x < dy; x++ {
        //once we are in the loop we have to make the first dimension of the array
        //based on the dx values
        image[x] = make([]uint8, dx)
        for y := 0; y < dx; y++ {
            //This is a function +to assign the pixel values to the array
            image[x][y] = uint8((x * y) /20)
        }
    }
    return image
}

func main() {
    pic.Show(Pic)
}

【问题讨论】:

    标签: go


    【解决方案1】:

    假设iint 类型,uint8(i) 返回Least Significant Byte (LSB)i

    x[0, 255]范围内时,意思是:0 &lt;= x &lt;= 255
    并且y 在 [0, 255] 范围内,
    那么x*y[0, 255*255] = [0, 65025] 范围内
    所以x*y/20[0, 255*255/20] = [0, 65025/20] = [0, 3251] 范围内
    uint8(x*y/20) 的值等于 (x*y/20)%256 表示 LSB 字节:
    uint8(3251) = uint8(0XCB3) = 0XB3 = 179
    3251 = 12*256 + 179

    所以每次x*y/20 大于 255 时,它都会重新从 0 开始计数:(x*y/20) % 256 这就是为什么您的图像会重复圆圈。

    试试这个工作示例代码:

    package main
    
    import "fmt"
    
    func main() {
        for y := 0; y <= 255; y++ {
            for x := 0; x <= 255; x++ {
                v := x * y / 20
                if int(uint8(v)) != v%256 {
                    fmt.Println(v, v%256)
                }
            }
        }
        fmt.Println("Done.")
    }
    

    输出:

    Done.
    

    让我们简化你的例子,看看这个工作示例代码:

    package main
    
    import (
        "bytes"
        "image"
        "image/png"
        "os"
    )
    
    func main() {
        const dx = 256
        const dy = 256
        m := image.NewNRGBA(image.Rect(0, 0, dx, dy))
        for y := 0; y < dy; y++ {
            for x := 0; x < dx; x++ {
                v := uint8(x * y / 20)
                i := y*m.Stride + x*4
                m.Pix[i] = v     //R
                m.Pix[i+1] = v   //G
                m.Pix[i+2] = 255 //B
                m.Pix[i+3] = 255 //A
            }
        }
        var buf bytes.Buffer
        err := png.Encode(&buf, m)
        if err != nil {
            panic(err)
        }
        os.Stdout.Write(buf.Bytes())
    }
    

    并将输出重定向到像 main &gt; b.pnggo run main.go &gt; b.png 这样的文件
    查看输出文件b.png:

    【讨论】:

    • @deltaskelta 为什么输入uint8(256)会失败?是的uint8(256) 给出constant 256 overflows uint8var i int = 256 fmt.Println(uint8(i)) 给出0,请参阅:github.com/golang/go/issues/16153
    【解决方案2】:

    uint8(anotherIntValue) 转换将占用anotherIntValue 的最后一个字节。这就是为什么您的代码可以产生许多蓝色 (0)。例如,以下代码将打印 'val = 0'。

    dx, dy := 128, 2
    fmt.Println("val =", uint8(dx*dy))
    

    编译器将检查常量转换是否存在超出范围的错误。

    【讨论】:

    • 那么为什么当我输入uint8(256)..它说它溢出uint8时它会失败
    • 这是因为常量转换受制于不同的静态检查规则。可以在golang.org/ref/spec#Conversions 找到参考。具体来说,当 x 是常数并且“x 可以用 T 类型的值表示”时,T(x) 是好的。 int8(256) 失败,因为 256 超出了一个字节可以表示的范围。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-11
    • 1970-01-01
    • 2023-01-27
    • 2020-08-10
    • 1970-01-01
    相关资源
    最近更新 更多