惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

有赞技术团队
有赞技术团队
V
V2EX
Jina AI
Jina AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
宝玉的分享
宝玉的分享
S
SegmentFault 最新的问题
量子位
Engineering at Meta
Engineering at Meta
Forbes - Security
Forbes - Security
H
Hackread – Cybersecurity News, Data Breaches, AI and More
B
Blog
I
Intezer
V
Vulnerabilities – Threatpost
NISL@THU
NISL@THU
P
Proofpoint News Feed
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
人人都是产品经理
人人都是产品经理
The Cloudflare Blog
博客园 - 三生石上(FineUI控件)
Google DeepMind News
Google DeepMind News
T
Tenable Blog
Know Your Adversary
Know Your Adversary
Cisco Talos Blog
Cisco Talos Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Scott Helme
Scott Helme
Stack Overflow Blog
Stack Overflow Blog
博客园 - 【当耐特】
S
Securelist
T
Tailwind CSS Blog
Simon Willison's Weblog
Simon Willison's Weblog
Microsoft Security Blog
Microsoft Security Blog
博客园_首页
P
Privacy International News Feed
K
Kaspersky official blog
T
Tor Project blog
L
LINUX DO - 热门话题
Apple Machine Learning Research
Apple Machine Learning Research
T
The Exploit Database - CXSecurity.com
Security Latest
Security Latest
酷 壳 – CoolShell
酷 壳 – CoolShell
C
Cybersecurity and Infrastructure Security Agency CISA
T
Threat Research - Cisco Blogs
G
GRAHAM CLULEY
Last Week in AI
Last Week in AI
L
LangChain Blog
C
Cisco Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
N
Netflix TechBlog - Medium
博客园 - 叶小钗
I
InfoQ

hsfzxjy 的博客

解决 VSCode + CMake + MSVC 编译器信息乱码的问题 使用 3090 部署 1.58bit 动态量化版 DeepSeek R1 671b 如何在 VS Code DevContainer 中配置 HTTP 代理 如何在跳板机背后的服务器上使用 VS Code Remote - Containers Cohesive Digests for Ints and Floats Rust 中的隐匿概念 —— Place(位置) 美术馆 一尺之槌,日取其半,1075日而竭 老生常谈:使用 Cloudflare 自选 IP 加速站点访问 辩义 State、Nation 与 Country 将 Base64 编码的数据快速转换为 Uint8Array 折腾 NPU·第1章 —— 搭建 Level Zero 开发环境 折腾 NPU·第0章 —— Intel NPU 概述与 Level-Zero 新增域名 monad.run CSS 中为特定字符设置不同字体 Arbitary Lifetime Transmutation via Rust Unsoundness Dijkstra 算法的延伸 Manacher 回文计数算法 硬卧 Go Fact: Zero-sized Field at the Rear of a Struct Has Non-zero Size 代码的仪式 Building Electron From Scratch 中式亲属称谓研究之一:构建半群 Some Notes on Kotlin Coroutines Git sparse-checkout and partial clones for Mega-Repos 辩义“封建” Diving from the CUDA Error 804 into a bug of libnvidia-container Modern Cryptography, GPG and Integration with Git(hub) Move the Root Partition of Ubuntu A New Programmer Kicks a Roadblock Git-based Dependencies in Dart and Go Reversy Naming 人类一败涂地 Invalid Golang Pointers Can Bite You Even If You Don't Dereference Side Project(副业) A Flaw of Promoting Complex Trait Bounds in Rust Initialize Process Pool Worker with Individual Value Rust - Python FFI From Scratch [Extending Hexo For My Site] Part 1 [Extending Hexo For My Site] Part 0 Debug a 'torch.tensor(1).cuda()' hanging 不自由的互联网 Retrieve Contents over HTTP without curl or wget [Unravelling mocona] Part 1 - Verbosity or Anti-Pattern [Unravelling mocona] Part 0 - Preface Understanding pickle in Python Rough Notes on Deploying Vaultwarden & NextCloud Bookmarks 语言狂热者与实用主义者 Demystify the randomness in CUDA kernels Performant Bulk Mutations in IndexedDB Auto Rebuild .pyx Files with pyximport Cython and Threads Obtain a Random Available TCP Port with Bash Information Theory: KL Divergence Information Theory: Entropy and Mutual Information 铁板烧 西郊线 Proof of the Gumbel Max Trick Option::as_ref Rc, RefCell and Interior Mutability Visualizing Correlation 三月十日杂感 三月一日杂感 二月十一日杂感 一月二十六日杂感 SS Configuration 一月七日杂感 四月·病 Haskell 笔记:State Monad Haskell 笔记:Monad 引论 Haskell 笔记:Applicative Haskell 笔记:Category Theory and Functor Haskell 笔记:data, type, newtype Haskell 笔记:folds 使用 Aria2 在 Ubuntu 中下载百度云资源 从伪并行的 Python 多线程说起 一个 Reentrant Error 引发的对 Python 信号机制的探索和思考 Linux 文件权限 HSFZMUN 4.0 部署小记 午后雨·科大 最后的雨夜·广州 揭秘·变态的平方根倒数算法 神坑·Python 装饰类无限递归 Python“黑魔法”之 Encoding & Decoding Ubuntu 重新映射键盘布局 为什么我要翻墙 Python“黑魔法”之 Generator Coroutines 数学美 之 判断线段相交的最简方法 除夕杂感 17 行代码实现的简易 Javascript 字符串模板 Python“黑魔法”之 Meta Classes 诗集 生活,需要被“发现” 家书·十八岁成人礼 炫技?还是需求? 【译】响应式图片的现状 【译】“为什么有这么多的编程语言?” Wisecity 商赛总结——也谈前端自动化测试 记一次 DoS 诈骗网站的经历 那一年,我们望向星空
Display *big.Rat Losslessly and Smartly in Golang
2023-10-10 · via hsfzxjy 的博客

Floating-point numbers, as we know, are notorious for losing precision when their values become too large or too small. They are also bad at representing decimals accurately, yielding confusions like 0.1 + 0.2 != 0.3 for every beginner in their programming 101.

Albeit being imprecise, floats are good enough for most daily scenarios. For those not, however, Golang provides *big.Rat to the rescue. Rats are designed to represent rational numbers with arbitary precision, addressing most flaws of floats, yet at a cost of much slower computation speed and bigger memory footprint. For example, we are confident to compare 0.1 + 0.2 to 0.3 using Rats without caring about tolerance:

package main

import (
"fmt"
"math/big"
)

func main() {
x, y := 0.1, 0.2
f := x + y
fmt.Printf("%.20f %v\n", f, f == 0.3)


a, b := new(big.Rat).SetFrac64(1, 10), new(big.Rat).SetFrac64(2, 10)
c := new(big.Rat).Add(a, b)
c2 := new(big.Rat).SetFrac64(3, 10)
fmt.Printf("%s %v\n", c.FloatString(20), c.Cmp(c2) == 0)

}

You may have noticed that the Rats are initialized by the z.SetFrac64(a, b) method, which sets z to the fractional number a/b. In fact there’s even a z.SetString() to parse a Rat from either its fractional or decimal representation, which is a convenient utility:

r1, ok1 := new(big.Rat).SetString("3/5")
r2, ok2 := new(big.Rat).SetString("0.6")

In the above listing, both r1 and r2 equal to the same number of 0.6. .SetString() smartly infers the input format and performs parsing.

Now let’s think about the reversed problem – how to display a *big.Rat as string smartly and loselessly?

To clarify, we would like to obtain a string s from the given Rat z. s is formatted as decimal when z could be written as finite decimal, and otherwise formatted as fractional. If we give name SmartRatString() to such a function, some samples may be:

SmartRatString(new(big.Rat).SetFrac64(3, 5)) == "0.6"
SmartRatString(new(big.Rat).SetFrac64(1, 3)) == "1/3"

This is a legitimate use case. You may want to print out some numbers to the user, and expect they would be parsed exactly as they were if being typed back, for the motive of reproducibility or whatever. Simultaneously, the numbers should be in decimal form whenever they could, to conform the preference of human.

Unfortunately, *big.Rat does not come with such conversion method. The most relevant ones we could find are RatString() and FloatString(prec). RatString() always converts the Rat into fractional form a/b, while FloatString(prec) displays it as decimal form with exactly prec digits after the decimal point.

A straightforward thought is to combine the two utility methods in an adaptive way. If the Rat couldn’t be written as finite decimal, we call the RatString(). Otherwise, we compute the appropriate prec for FloatString() such that the Rat is converted into decimal form without any truncation. In such a way, we reduce the problem into two simpler ones:

  1. How to determine a Rat has a finite decimal representation?
  2. How to compute the number of digits after the decimal point?

Answers to both problems concealed in the factorization of the denominator. Say we have a rational number $z=a/b$ where $b \in \mathbb{Z}^+$ and $gcd(a, b)=1$. $z$ has a finite decimal form if and only if $b=2^n5^m$ for some natural numbers $n$ and $m$, while $\max(n, m)$ being the number of digits after the decimal point. These conclusions can be derived from some easy math so we won’t discuss in this post.

The following section will focus on the implementation. We can sketch out the framework of SmartRatString():

func SmartRatString(r *big.Rat) string {
denom := new(big.Int).Set(r.Denom())
n := ...

m, isFiveExp := ...
if !isFiveExp {
return r.RatString()
}
return r.FloatString(int(max(n, m)))
}

Estimating n is the easiest part. We can compute n by counting zero bits at the rear of denom‘s binary form, with the help of TrailingZeroBits() method. Dividing denom by 2^n can also be achieved efficiently with bitwise right shifting. We complete the first blank as follows:

func SmartRatString(r *big.Rat) string {
denom := new(big.Int).Set(r.Denom())
n := denom.TrailingZeroBits()
denom.Rsh(&denom, n)
m, isFiveExp := ...
if !isFiveExp {
return r.RatString()
}
return r.FloatString(int(max(n, m)))
}

For the second part, however, there’s no shortcut, at least I don’t have an idea. We have to iteratively divide denom by 5 until the process cannot proceed. For readability I write a small function log5:

var intOne = new(big.Int).SetUint64(1)
var intFive = new(big.Int).SetUint64(5)



func log5(x *big.Int) (cnt uint, isExp bool) {
tmp2 := new(big.Int)
m := new(big.Int)
for x.CmpAbs(intOne) > 0 {
tmp2.DivMod(x, intFive, m)
if m.Sign() != 0 {
return cnt, false
}
cnt++
x, tmp2 = tmp2, x
}
return cnt, true
}

This function is not efficient but it serves the purpose. We modify the second part of SmartRatString() accordingly:

var intOne = new(big.Int).SetUint64(1)
var intFive = new(big.Int).SetUint64(5)



func log5(x *big.Int) (cnt uint, isExp bool) {
tmp2 := new(big.Int)
m := new(big.Int)
for x.CmpAbs(intOne) > 0 {
tmp2.DivMod(x, intFive, m)
if m.Sign() != 0 {
return cnt, false
}
cnt++
x, tmp2 = tmp2, x
}
return cnt, true
}

func SmartRatString(r *big.Rat) string {
denom := new(big.Int).Set(r.Denom())
n := denom.TrailingZeroBits()
denom.Rsh(&denom, n)
m, isFiveExp := log5(&denom)
if !isFiveExp {
return r.RatString()
}
return r.FloatString(int(max(n, m)))
}

With all these in hand, we have finished the complete function of SmartRatString().


Author: hsfzxjy.
Link: .
License: CC BY-NC-ND 4.0.
All rights reserved by the author.
Commercial use of this post in any form is NOT permitted.
Non-commercial use of this post should be attributed with this block of text.