int64(math.Pow(2, 63) – 1) 的结果是 -9223372036854775808,而不是 9223372036854775807。

huangapple go评论92阅读模式
英文:

int64(math.Pow(2, 63) - 1) results in -9223372036854775808 rather than 9223372036854775807

问题

我正在尝试存储不同位数的最大和最小有符号整数。这段代码对于除了int64之外的整数工作得很好。

package main

import (
	"fmt"
	"math"
)

func main() {
	var minInt8 int8 = -128
	var maxInt8 int8 = 127
	fmt.Println("int8\t->", minInt8, "to", maxInt8)
	fmt.Println("int8\t->", math.MinInt8, "to", math.MaxInt8)

	var minInt16 int16 = int16(math.Pow(-2, 15))
	var maxInt16 int16 = int16(math.Pow(2, 15) - 1)
	fmt.Println("int16\t->", minInt16, "to", maxInt16)
	fmt.Println("int16\t->", math.MinInt16, "to", math.MaxInt16)

	var minInt32 int32 = int32(math.Pow(-2, 31))
	var maxInt32 int32 = int32(math.Pow(2, 31) - 1)
	fmt.Println("int32\t->", minInt32, "to", maxInt32)
	fmt.Println("int32\t->", math.MinInt32, "to", math.MaxInt32)

	var minInt64 int64 = int64(math.Pow(-2, 63))
	var maxInt64 int64 = int64(math.Pow(2, 63) - 1) // 给出了错误的输出
	fmt.Println("int64\t->", minInt64, "to", maxInt64)
	fmt.Println("int64\t->", math.MinInt64, "to", math.MaxInt64)
}

输出:

int8    -> -128 to 127
int8    -> -128 to 127
int16   -> -32768 to 32767
int16   -> -32768 to 32767
int32   -> -2147483648 to 2147483647
int32   -> -2147483648 to 2147483647
int64   -> -9223372036854775808 to -9223372036854775808
int64   -> -9223372036854775808 to 9223372036854775807

我不知道这种行为的原因,希望能得到帮助。

英文:

I am trying to store max and min signed ints of different bits. The code works just fine for ints other than int64

package main

import (
	"fmt"
	"math"
)

func main() {
	var minInt8 int8 = -128
	var maxInt8 int8 = 127
	fmt.Println("int8\t->", minInt8, "to", maxInt8)
	fmt.Println("int8\t->", math.MinInt8, "to", math.MaxInt8)

	var minInt16 int16 = int16(math.Pow(-2, 15))
	var maxInt16 int16 = int16(math.Pow(2, 15) - 1)
	fmt.Println("int16\t->", minInt16, "to", maxInt16)
	fmt.Println("int16\t->", math.MinInt16, "to", math.MaxInt16)

	var minInt32 int32 = int32(math.Pow(-2, 31))
	var maxInt32 int32 = int32(math.Pow(2, 31) - 1)
	fmt.Println("int32\t->", minInt32, "to", maxInt32)
	fmt.Println("int32\t->", math.MinInt32, "to", math.MaxInt32)

	var minInt64 int64 = int64(math.Pow(-2, 63))
	var maxInt64 int64 = int64(math.Pow(2, 63) - 1) // gives me the wrong output
	fmt.Println("int64\t->", minInt64, "to", maxInt64)
	fmt.Println("int64\t->", math.MinInt64, "to", math.MaxInt64)
}

Output:

int8    -> -128 to 127
int8    -> -128 to 127
int16   -> -32768 to 32767
int16   -> -32768 to 32767
int32   -> -2147483648 to 2147483647
int32   -> -2147483648 to 2147483647
int64   -> -9223372036854775808 to -9223372036854775808
int64   -> -9223372036854775808 to 9223372036854775807

I have no idea about the cause of this behavior, any help would be appreciated.

答案1

得分: 2

这里有多个问题:

math.Pow 返回一个 float64 类型。这种类型不能用于表示一个64位有符号整数,以满足此处所需的完全精度计算。引用自双精度浮点数格式

> 整数值的精度限制
> - 可以精确表示从-2^53到2^53(-9,007,199,254,740,992到9,007,199,254,740,992)的整数
> - 介于2^53和2^54 = 18,014,398,509,481,984之间的整数会四舍五入为2的倍数(偶数)
> - 介于2^54和2^55 = 36,028,797,018,963,968之间的整数会四舍五入为4的倍数

即使精度足够(在2^63的特殊情况下是正确的),float64 的精度也不足以从2^63中减去1。只需尝试以下操作(此处使用 uint64,因为有符号 int64 不足够):

uint64(math.Pow(2, 63))    // -> 9223372036854775808
uint64(math.Pow(2, 63)-1)  // -> 9223372036854775808

首先将值转换为 uint64,然后再进行减法运算可以解决这个问题,但这仅适用于2^63,因为其他具有相同大小的值无法在 float64 中以完全精度表示:

uint64(math.Pow(2, 63))-1  // -> 9223372036854775807
英文:

There are multiple problems here:

math.Pow returns a float64. This type cannot be used to represent a 64 bit signed integer with full precision as required for the attempted computation here. To cite from Double-precision floating-point format

> Precision limitations on integer values
> - Integers from −2^53 to 2^53 (−9,007,199,254,740,992 to 9,007,199,254,740,992) can be exactly
> represented
> - Integers between 2^53 and 2^54 = 18,014,398,509,481,984
> round to a multiple of 2 (even number)
> - Integers between 2^54 and 2^55 =
> 36,028,797,018,963,968 round to a multiple of 4

Even if the precision would be sufficient (which is true in the special case of 2^63) then the precision of float64 is not sufficient to substract 1 from 2^63. Just try the following (uint64 is used here since signed int64 is not sufficient):

uint64(math.Pow(2, 63))    // -> 9223372036854775808
uint64(math.Pow(2, 63)-1)  // -> 9223372036854775808

Converting the value first to uint64 and then subtracting works instead, but only because 2^63 can be represented with full prevision in float64 even though other values with this size can not:

uint64(math.Pow(2, 63))-1  // -> 9223372036854775807

huangapple
  • 本文由 发表于 2022年7月9日 16:59:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/72920132.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定