英文:
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 (
"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))
}
Output:
95000015877CD001
10736581604118679553 <nil>
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 (
"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)
}
Output:
1.073658160411868e+19 <nil>
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论