如何将math.Inf强制转换为整数?

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

How to coerce math.Inf to an integer?

问题

我有一些用于比较的代码,并且我想从无穷大的值开始。这是我的代码片段。

import (
	"fmt"
	"math"
)

func snippet(arr []int) {
	least := int(math.Inf(1))
	greatest := int(math.Inf(-1))
	fmt.Println("least", math.Inf(1), least)
	fmt.Println("greatest", math.Inf(-1), greatest)
}

这是我从控制台得到的输出:

least +Inf -9223372036854775808
greatest -Inf -9223372036854775808

为什么+Inf被强制转换为负数的int

英文:

I've got some code I'm using to do comparisons, and I want to start with infinite values. Here's a snippet of my code.

import (
	"fmt"
	"math"
)
func snippet(arr []int) {
	least := int(math.Inf(1))
	greatest := int(math.Inf(-1))
	fmt.Println("least", math.Inf(1), least)
	fmt.Println("greatest", math.Inf(-1), greatest)
}

and here's the output I get from the console

least +Inf -9223372036854775808
greatest -Inf -9223372036854775808

why is +Inf coerced into a negative int ?

答案1

得分: 1

无穷大在int中是无法表示的。
根据Go语言规范,

> 在所有涉及浮点数或复数值的非常量转换中,如果结果类型无法表示该值,则转换成功,但结果值是实现相关的。

也许你正在寻找最大可表示的int?如何获取它在这里有解释:这里

英文:

Infinity is not representable by int.
According to the go spec,

> In all non-constant conversions involving floating-point or complex values, if the result type cannot represent the value the conversion succeeds but the result value is implementation-dependent.

Maybe you are looking for the largest representable int? How to get it is explained here.

答案2

得分: 0

math.Inf()函数返回一个IEEE双精度浮点数,如果参数的符号大于等于0,则表示正无穷大,如果符号小于0,则表示负无穷大,所以你的代码是错误的。

但是,Go语言规范(阅读规范总是好的)中指出:
> 数值类型之间的转换
> .
> .
> .
> 在涉及浮点数或复数值的所有非常量转换中,
> 如果结果类型无法表示该值,则转换成功,但结果值是实现相关的。

补码整数值没有无穷大的概念,所以结果是与实现相关的。

我个人期望得到的是目标整数类型的最大或最小整数值,但显然情况并非如此。

这是负责转换的运行时源文件:https://go.dev/src/runtime/softfloat64.go

以下是实际的源代码。

请注意,IEEE-754双精度浮点数是一个64位双字,由以下部分组成:

  • 符号位,即高位(最高有效位/最左边位),0表示正数,1表示负数。
  • 指数(偏置),由接下来的11位组成,
  • 尾数,由剩余的52位组成,可以是非规格化的。

正无穷大是一个特殊值,其符号位为0,指数位全为1,尾数位全为0

0 11111111111 0000000000000000000000000000000000000000000000000000

0x7FF0000000000000

负无穷大与之相同,唯一的区别是符号位为1:

1 11111111111 0000000000000000000000000000000000000000000000000000

0xFFF0000000000000

看起来 funpack64() 返回5个值:

  • 一个表示_符号_(0或非常大的非零值0x8000000000000000)的 uint64
  • 一个表示规格化_尾数_的 uint64
  • 一个表示_指数_的 int
  • 一个表示是否为正负无穷大的 bool
  • 一个表示是否为NaNbool

通过这些信息,你应该能够弄清楚为什么它返回这个值。

[坦率地说,当 funpack64() 返回 fi = true 时,我很惊讶 f64toint() 没有立即停止运行。]

const mantbits64 uint = 52
const expbits64  uint = 11
const bias64          = -1<<(expbits64-1) + 1

func f64toint(f uint64) (val int64, ok bool) {
	fs, fm, fe, fi, fn := funpack64(f)

	switch {
	case fi, fn: // NaN
		return 0, false

	case fe < -1: // f < 0.5
		return 0, false

	case fe > 63: // f >= 2^63
		if fs != 0 && fm == 0 { // f == -2^63
			return -1 << 63, true
		}
		if fs != 0 {
			return 0, false
		}
		return 0, false
	}

	for fe > int(mantbits64) {
		fe--
		fm <<= 1
	}
	for fe < int(mantbits64) {
		fe++
		fm >>= 1
	}
	val = int64(fm)
	if fs != 0 {
		val = -val
	}
	return val, true
}

func funpack64(f uint64) (sign, mant uint64, exp int, inf, nan bool) {
	sign = f & (1<<(mantbits64 + expbits64))
	mant = f & (1<<mantbits64 - 1)
	exp = int(f>>mantbits64) & (1<<expbits64 - 1)

	switch exp {
	case 1<<expbits64 - 1:
		if mant != 0 {
			nan = true
			return
		}
		inf = true
		return

	case 0:
		// denormalized
		if mant != 0 {
			exp += bias64 + 1
			for mant < 1<<mantbits64 {
				mant <<= 1
				exp--
			}
		}

	default:
		// add implicit top bit
		mant |= 1 << mantbits64
		exp += bias64
	}
	return
}
英文:

math.Inf() returns an IEEE double-precision float representing positive infinity if the sign of the argument is >= 0, and negative infinity if the sign is < 0, so your code is incorrect.

But, the Go language specifiction (always good to read the specifications) says this:
> Conversions between numeric types
> .
> .
> .
> In all non-constant conversions involving floating-point or complex values,
> if the result type cannot represent the value the conversion succeeds but
> the result value is implementation-dependent.

Two's complement integer values don't have the concept of infinity, so the result is implementation dependent.

Myself, I'd have expected to get the largest or smallest integer value for the integer type the cast is targeting, but apparently that's not the case.

This looks to the runtime source file responsible for the conversion, https://go.dev/src/runtime/softfloat64.go

And this is the actual source code.

Note that an IEEE-754 double-precision float is a 64-bit double word, consisting of

  • a sign bit, the high-order (most significant/leftmost bit), 0 indicating positive, 1 indicating negative.
  • an exponent (biased), consisting of the next 11 bits, and
  • a mantissa, consisting of the remaining 52 bits, which can be denormalized.

Positive Infinity is a special value with a sign bit of 0, a exponent of all 1 bits, and a mantissa of all 0 bits:

0 11111111111 0000000000000000000000000000000000000000000000000000

or 0x7FF0000000000000.

Negative infinity is the same, with the exception that the sign bit is 1:

1 11111111111 0000000000000000000000000000000000000000000000000000

or 0xFFF0000000000000.

Looks like `funpack64() returns 5 values:

  • a uint64 representing the sign (0 or the very large non-zero value 0x8000000000000000),
  • a uint64 representing the normalized mantissa,
  • an int representing the exponent,
  • a bool indicating whether or not this is +/- infinity, and
  • a bool indicating whether or not this is NaN.

From that, you should be able to figure out why it returns the value it does.

[Frankly, I'm surprised that f64toint() doesn't short-circuit when funpack64() returns fi = true.]

const mantbits64 uint = 52
const expbits64  uint = 11
const bias64          = -1&lt;&lt;(expbits64-1) + 1

func f64toint(f uint64) (val int64, ok bool) {
	fs, fm, fe, fi, fn := funpack64(f)

	switch {
	case fi, fn: // NaN
		return 0, false

	case fe &lt; -1: // f &lt; 0.5
		return 0, false

	case fe &gt; 63: // f &gt;= 2^63
		if fs != 0 &amp;&amp; fm == 0 { // f == -2^63
			return -1 &lt;&lt; 63, true
		}
		if fs != 0 {
			return 0, false
		}
		return 0, false
	}

	for fe &gt; int(mantbits64) {
		fe--
		fm &lt;&lt;= 1
	}
	for fe &lt; int(mantbits64) {
		fe++
		fm &gt;&gt;= 1
	}
	val = int64(fm)
	if fs != 0 {
		val = -val
	}
	return val, true
}

func funpack64(f uint64) (sign, mant uint64, exp int, inf, nan bool) {
	sign = f &amp; (1 &lt;&lt; (mantbits64 + expbits64))
	mant = f &amp; (1&lt;&lt;mantbits64 - 1)
	exp = int(f&gt;&gt;mantbits64) &amp; (1&lt;&lt;expbits64 - 1)

	switch exp {
	case 1&lt;&lt;expbits64 - 1:
		if mant != 0 {
			nan = true
			return
		}
		inf = true
		return

	case 0:
		// denormalized
		if mant != 0 {
			exp += bias64 + 1
			for mant &lt; 1&lt;&lt;mantbits64 {
				mant &lt;&lt;= 1
				exp--
			}
		}

	default:
		// add implicit top bit
		mant |= 1 &lt;&lt; mantbits64
		exp += bias64
	}
	return
}

huangapple
  • 本文由 发表于 2022年8月16日 06:35:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/73367050.html
匿名

发表评论

匿名网友

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

确定