Golang 十进制转十六进制的错误

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

golang decimal to hex conversion error

问题

精度问题

设置是一个物理的i-button(1-wire规范),制造商打印的十六进制值(可信赖的值)。一个物联网设备将二进制编码为十进制数 - Golang将接收到的值返回为十六进制数。

接收到的十进制值是:

var V = 10736581604118680000
fmt.Sprintf("%016X", m.V)[2:14]) // 添加大写字母并截断输出

提供的结果是 000015877CD1

预期的可信输出是 000015877CD0

钥匙上刻的十六进制是 95 000015877CD0 01

http://www.rapidtables.com/convert/number/decimal-to-hex.htm(可信赖吗?)表明使用的golang函数失去了精度。将编码为19位十进制数字的二进制值转换为十六进制,Golang可以在不丢失精度的情况下进行(使用上述函数)。

英文:

Problem with precision

The setup is a physical i-button (1-wire specification) with printed hex value by manufacturer (trusted value). An IoT device encodes the binary as decminal - Golang returns the received value to Hex.

received decimal value is:

var V = 10736581604118680000
fmt.Sprintf("%016X", m.V)[2:14]) // adds uppercase and truncation of output

provides 000015877CD1

The expected trusted output is 000015877CD0

the engraved Hex on the key is 95 000015877CD0 01

http://www.rapidtables.com/convert/number/decimal-to-hex.htm (trusted?) indicates that the golang function used has lost precision. binary values that encode to 19 decimal digits can be converted to Hex by Golang without loss of precision (using function above)

答案1

得分: 2

例如,

package main

import (
	"fmt"
	"math/big"
	"strconv"
)

func hexdec(s string) uint64 {
	d := uint64(0)
	for i := 0; i < len(s); i++ {
		x := uint64(s[i])
		if x >= 'a' {
			x -= 'a' - 'A'
		}
		d1 := x - '0'
		if d1 > 9 {
			d1 = 10 + d1 - ('A' - '0')
		}
		if 0 > d1 || d1 > 15 {
			panic("hexdec")
		}
		d = (16 * d) + d1
	}
	return d
}

func main() {
	x := "95000015877CD001"
	fmt.Println(x)
	n, err := strconv.ParseUint(x, 16, 64)
	fmt.Println(n, err)
	s := fmt.Sprintf("%016X", n)[2:14]
	fmt.Println(s)
	z, t := big.NewInt(0).SetString(x, 16)
	fmt.Println(z, t)
	s = fmt.Sprintf("%016X", z)[2:14]
	fmt.Println(s)
	fmt.Println(hexdec(x))
}

输出:

95000015877CD001
10736581604118679553 <nil>
000015877CD0
10736581604118679553 true
000015877CD0
10736581604118679553

请注意,您接近64位整数的限制:

uint64 为所有无符号64位整数的集合(从0到18446744073709551615)

那么

Var V = 10736581604118680000

是从哪里来的?


数字10736581604118680000是整数10736581604118679553的浮点数近似值(1.073658160411868e+19)。制造商可能不理解浮点数:计算机科学家应该了解的浮点数算术知识。给定整数10736581604118680000,Go计算出了正确的结果。Go在数学上是正确的。

所以让我们尝试告诉Go,10736581604118680000不是一个精确的整数,而是一个近似的浮点数。

例如,

package main

import (
	"fmt"
	"strconv"
)

func main() {
	d := "10736581604118680000"
	f, err := strconv.ParseFloat(d, 64)
	fmt.Println(f, err)
	z := uint64(f)
	fmt.Println(z)
	s := fmt.Sprintf("%016X", z)[2:14]
	fmt.Println(s)
}

输出:

1.073658160411868e+19 <nil>
10736581604118679552
000015877CD0

虽然这个技巧在这种情况下有效,但不能保证在所有情况下都有效。真正的解决方案是制造商雇用一些有能力的数学家。我想知道他们的软件和硬件中还有哪些其他错误。

英文:

For example,

package main

import (
	&quot;fmt&quot;
	&quot;math/big&quot;
	&quot;strconv&quot;
)

func hexdec(s string) uint64 {
	d := uint64(0)
	for i := 0; i &lt; len(s); i++ {
		x := uint64(s[i])
		if x &gt;= &#39;a&#39; {
			x -= &#39;a&#39; - &#39;A&#39;
		}
		d1 := x - &#39;0&#39;
		if d1 &gt; 9 {
			d1 = 10 + d1 - (&#39;A&#39; - &#39;0&#39;)
		}
		if 0 &gt; d1 || d1 &gt; 15 {
			panic(&quot;hexdec&quot;)
		}
		d = (16 * d) + d1
	}
	return d
}

func main() {
	x := &quot;95000015877CD001&quot;
	fmt.Println(x)
	n, err := strconv.ParseUint(x, 16, 64)
	fmt.Println(n, err)
	s := fmt.Sprintf(&quot;%016X&quot;, n)[2:14]
	fmt.Println(s)
	z, t := big.NewInt(0).SetString(x, 16)
	fmt.Println(z, t)
	s = fmt.Sprintf(&quot;%016X&quot;, z)[2:14]
	fmt.Println(s)
	fmt.Println(hexdec(x))
}

Output:

95000015877CD001
10736581604118679553 &lt;nil&gt;
000015877CD0
10736581604118679553 true
000015877CD0
10736581604118679553

Note that you are near the limits of 64-bit integers:

uint64  the set of all unsigned 64-bit integers (0 to 18446744073709551615)

Where does

Var V = 10736581604118680000

come from?


The number 10736581604118680000 is a floating-point approximation (1.073658160411868e+19) of the integer 10736581604118679553. The manufacturer probably doesn't understand floating-point: What Every Computer Scientist Should Know About Floating-Point Arithmetic. Given the integer 10736581604118680000, Go calculates the correct result. Go is mathematically correct.

So let's try telling Go that 10736581604118680000 is not an exact integer, that it's an approximate floating-point number.

For example,

package main

import (
	&quot;fmt&quot;
	&quot;strconv&quot;
)

func main() {
	d := &quot;10736581604118680000&quot;
	f, err := strconv.ParseFloat(d, 64)
	fmt.Println(f, err)
	z := uint64(f)
	fmt.Println(z)
	s := fmt.Sprintf(&quot;%016X&quot;, z)[2:14]
	fmt.Println(s)
}

Output:

1.073658160411868e+19 &lt;nil&gt;
10736581604118679552
000015877CD0

While this trick works in this case, it's not guaranteed to work in all cases. The real solution is for the manufacturer to employ some competent mathematicians. I wonder what other bugs there are in their software and hardware.

huangapple
  • 本文由 发表于 2014年11月29日 10:58:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/27198396.html
匿名

发表评论

匿名网友

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

确定