Golang中如何修改浮点数指数的基数?

Golang中如何修改浮点数指数的基数? 如何在Go中将浮点数的指数基数从10进制改为5/8/16进制或任意进制?数据类型当然是float32/float64

已知数学公式为:

B^y = 10^x
y = x*logB(10)  --- [1]

∴,
base5:  y = x*log5(10)
base8:  y = x*log8(10)
base16: y = x*log16(10)
7 回复

你是指字面量吗?我怀疑你能改变这个。

更多关于Golang中如何修改浮点数指数的基数?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


NobbZ:

你指的是字面量吗?

是的。这是为了将整个浮点数(默认为十进制)转换为X进制,以防数字过大且以指数形式表示。

Cory: 目前看起来不错。会继续努力降低除法运算的成本。

既然我们已经得到了答案,而且降低除法成本是可以后续进行的优化,我就关闭这个话题了。一切顺利,谢谢!

func main() {
    fmt.Println("hello world")
}

Go 1.13 版本支持十六进制浮点数字面量

十六进制浮点数字面量由 0x0X 前缀、整数部分(十六进制数字)、小数点、小数部分(十六进制数字)和指数部分(pP 后接可选符号和十进制数字)组成。整数部分或小数部分可以省略;小数点也可以省略,但指数部分必须保留。(该语法符合 IEEE 754-2008 §5.12.3 标准。)指数值 exp 会将尾数(整数和小数部分)按 2 的 exp 次幂进行缩放。

Cory: 是的,这是为了将整个浮点数(默认为十进制)转换为X进制

math 包包含 log2 函数,你是否尝试过使用以下数学模型并四舍五入到最接近的数字?

B^y = 10^x
y = x*logB(10)  --- [1]

From [1],
y = ‖x*(log2(10) / log2(B))‖
y = ‖x*c, c = log2(10) / log2(B)‖    --- [2]

∴,
base2,
y = x*c, c = log2(10) / log2(2)
y = x*c, c = log2(10)
y = ‖x*log2(10)‖

base5,
y = x*log5(10)
y = ‖x*c, c = log2(10) / log2(5)‖

base8,
y = x*log8(10)
y = x*c, c = log2(10) / log2(8)
y = ‖x*c, c = log2(10) / 3‖

base16,
y = x*log16(10)
y = x*c, c = log2(10) / log2(16)
y = ‖x*c, c = log2(10) / 4‖

计算分为两个阶段:

  1. 根据目标基数 B 计算常数 c
  2. 使用给定的 x 计算 y

在计算 c 时,除法运算的成本相当高。构建预查找表是一种将所有除法转换为乘法的方法。

在代码中,应该类似于这样(未经测试):

func ExponentToBase(base uint, x float64) float64 {
	var c float64

	switch {
	case x == 0:
		return 0 // 无论基数如何,指数0始终等于0
	case base == 0:
		panic("base cannot be 0")// 基数不能为0
	case base == 1:
		panic("base cannot be 1") // log2(1)为0,这意味着log2(10)被0除
	case base == 2:
		c = ... // 查找预计算的log2(10)
	case base == 3:
		c = ... // 查找
	...
	case base == 8:
		c = ... // 查找预计算的log2(10) / 3
	...
	case base == 16:
		c = ... // 查找预计算的log2(10) / 4
	...
	default:
		c = math.log2(10) / math.log2(float64(base)) // 最后手段:计算常数
	}
	return math.Round(x*c)
}

calmh:

Go 1.13 版本支持十六进制浮点数字面量

感谢 @calmh。对于字面量解析我并不担心,让我担忧的是计算领域的问题。

hollowaykeanho:

计算分为两个阶段:

  1. 根据目标基数 B 计算常量 c
  2. 根据给定的 x 计算 y

我使用 ISO6093NR3 表示法进行了测试,目前看来效果不错,你说得对,除法运算的成本确实很高。

升级后的代码如下:

func ExponentToBase(base uint, x float64) uint64 {
	var c float64

	switch {
	case x == 0:
		return 1 // x^0 = y^0 = 1
	case base == 0:
		panic("base cannot be 0")// base cannot be 0
	case base == 1:
		panic("base cannot be 1") // log2(1) = 0. ∴, log2(10) / 0
	case base == 2:
		c = 3.321928094887362 //  log2(10)
	case base == 8:
		c = 1.1073093649624541 // log2(10) / 3
	case base == 10:
		return uint64(x)
	case base == 16:
		c = 0.8304820237218405 // log2(10) / 4
	default:
		c = math.log2(10) / math.log2(float64(base)) // calculate
	}
	return uint64(math.Round(x*c))
}

我也按照相同的模式创建了反对数函数。不过,现在的 c'1/c

func BaseToExponent(base uint, x float64) uint64 {
	var c float64

	switch {
	case x == 0:
		return 1 // x^0 = y^0 = 1
	case base == 0:
		panic("base cannot be 0")// base cannot be 0
	case base == 1:
		panic("base cannot be 1") // log2(1) = 0. ∴, log2(10) / 0
	case base == 2:
		c = 0.3010299956639812 //  1 / log2(10)
	case base == 8:
		c = 0.9030899869919435 // 3 / log2(10)
	case base == 10:
		return uint64(x)
	case base == 16:
		c = 1.2041199826559248 // 4 / log2(10)
	default:
		c = math.log2(float64(base)) / math.log2(10)  // calculate
	}
	return uint64(math.Round(x*c))
}

目前看起来不错。接下来将致力于降低除法运算的成本。

在Go语言中,浮点数在内存中是以二进制格式存储的,其指数部分基于2的幂次。你提到的公式 B^y = 10^xy = x * logB(10) 实际上描述的是数学上的基数转换,而不是直接修改浮点数的内部表示。Go标准库不直接支持修改浮点数指数的基数,但你可以通过数学计算来实现数值的基数转换。

具体来说,如果你有一个以10为基数的浮点数 x,并想将其转换为以 B 为基数的表示,你可以使用对数运算来计算对应的指数 y。公式为 y = x * log10(B) 的逆运算,但注意你的公式是 y = x * logB(10),这等价于 y = x / log10(B),因为 logB(10) = 1 / log10(B)。在Go中,你可以使用 math 包中的 Log10 函数来计算以10为底的对数。

以下是一个示例代码,展示如何将一个以10为基数的浮点数 x 转换为以任意基数 B 的指数形式。这里,我们假设 x 是原始值(例如,10^x 的形式),并计算 B^y 使得 B^y = 10^x,从而得到 y = x * log10(10) / log10(B),简化后为 y = x / log10(B)

package main

import (
    "fmt"
    "math"
)

// convertExponentBase 将浮点数从10为基数的指数转换为以base为基数的指数
// 输入x表示10^x,输出y表示base^y = 10^x
func convertExponentBase(x float64, base float64) float64 {
    // 使用公式: y = x / log10(base)
    // 因为 base^y = 10^x  =>  y * log10(base) = x  =>  y = x / log10(base)
    return x / math.Log10(base)
}

func main() {
    // 示例:将 10^3(即1000)转换为以5、8、16为基数的指数表示
    x := 3.0 // 表示 10^3
    base5 := 5.0
    base8 := 8.0
    base16 := 16.0

    y5 := convertExponentBase(x, base5)
    y8 := convertExponentBase(x, base8)
    y16 := convertExponentBase(x, base16)

    fmt.Printf("10^%.1f = 5^%.6f\n", x, y5)
    fmt.Printf("10^%.1f = 8^%.6f\n", x, y8)
    fmt.Printf("10^%.1f = 16^%.6f\n", x, y16)

    // 验证:计算 base^y 应该接近 10^x(即1000)
    fmt.Printf("验证 5^%.6f = %.6f (期望 ~1000)\n", y5, math.Pow(base5, y5))
    fmt.Printf("验证 8^%.6f = %.6f (期望 ~1000)\n", y8, math.Pow(base8, y8))
    fmt.Printf("验证 16^%.6f = %.6f (期望 ~1000)\n", y16, math.Pow(base16, y16))
}

输出示例:

10^3.0 = 5^4.292030
10^3.0 = 8^3.321928
10^3.0 = 16^2.500000
验证 5^4.292030 = 1000.000000 (期望 ~1000)
验证 8^3.321928 = 1000.000000 (期望 ~1000)
验证 16^2.500000 = 1000.000000 (期望 ~1000)

这个代码使用 math.Log10math.Pow 函数进行转换和验证。注意,由于浮点数的精度限制,结果可能会有微小误差,但在大多数应用中是可接受的。如果你处理的是 float32,只需将类型转换为 float32,但推荐使用 float64 以获得更高精度。

总之,Go中无法直接修改浮点数的内部指数基数,但通过数学计算可以模拟不同基数的指数表示。

回到顶部