![]() |
1
terryso 6 天前
好东西, 看看去
|
![]() |
2
MENGKE 6 天前
很有用,感谢分享
|
![]() |
3
pyyalt 6 天前
好用,支持!
|
![]() |
4
dssxzuxc 6 天前
确实 looks good, 源码才 113 行,实现方法很巧妙。
console.log(new Decimal('0.1').plus(new Decimal('0.2')).toString()) vs console.log(nstr(0.1+0.2)) 因为设计使然,超过 4 位(可自行设定)的 9 和 0 会被认为是噪音然后截断处理,不适用多位小数严格计算场景,其他场景都不错。 |
![]() |
5
retrocode 6 天前
另外如果有严格小数计算需求, 可以考虑 bignumber.js ,精度极高无任何误差, 在我以前某无聊的变态超大数计算项目里验证过, 小数点后精度 9999 无压力
|
![]() |
6
Valid 6 天前
好东西
|
7
mrlmh00 6 天前
我看样例都看不懂
显示不了 451.79 ? 451.79999999456789 = 451.8 451.78999999456789 = 451.78 451.77999999456789 = 451.78 451.76999999456789 = 451.77 |
![]() |
8
hamsterbase 6 天前 ![]() 不推荐这个库
推荐使用 decimal.js 实现精确的计算。 而不是计算以后猜数字 0.3 - 0.1 // 0.19999999999999998 x = new Decimal(0.3) x.minus(0.1) // '0.2' x // '0.3' |
![]() |
9
dssxzuxc 6 天前
@mrlmh00 #7
这个库只是用来处理 0.1+0.2=0.30000000000000004 这种问题的,让简单的浮点计算适合人类阅读 产生这种问题的浮点数都有个共同的特点:一连串的 0 或者 9 ,所以这个库就检测 0/9 连续出现的次数,超过判断条件后截断全部 0/9 最后简单处理一下进位 需要多位小数严谨计算的应该用 decimal.js, bignumber.js 这个库我觉得已经说得很清楚了,nstr ,就是数字转字符串,字符串是用来看的不是计算的,不需要严谨小数点计算的前端纯展示场景非常非常多 |
10
chesha1 6 天前
为啥不用成熟的高精度库呢,这个库的规则太简单粗暴了
|
![]() |
11
dssxzuxc 6 天前 ![]() @chesha1 #10 我觉得我在 4#的例子已经很有说服力了,在你想展示几个浮点数的计算结果时,你希望写的是
new Decimal(a).plus(new Decimal(b).times(new Decimal(c))).minus(new Decimal(d)) 还是 nstr(a+b*c-d) |
![]() |
12
Mint0315 6 天前
有用!
|
![]() |
14
dssxzuxc 6 天前
@mrlmh00 #13 研究了一下
https://github.com/shuding/nstr/blob/main/src/index.ts#L64-L77 进位失败是因为 451.78+0.01 = 451.78999999999996 而不是期望的 451.79 需要一个更稳健的进位方法来解决这个问题 |
15
chesha1 6 天前
@dssxzuxc #11 我会在后端做好 let x = new Decimal(a).plus(new Decimal(b).times(new Decimal(c))).minus(new Decimal(d)),然后让前端 {x}
|
![]() |
16
UnluckyNinja 6 天前
说计算精度需求的都跑偏了,网站里面的示例很清楚了,就是为了解决数字格式化的问题,避免因为精度误差导致显示的数字过长或过早使用科学计数法.
不过 100 多行还是有点多了,网站的对比看下来,toFixed 其实很符合要求,只是不会移除尾部 0 ,那么其实再替换下尾部 0 就够了,一行解决 200.0003.toFixed(3).replace(/(\.?|(?<=\.\d+))0+$/,'') (?<=\.\d+) 就是确保处于小数部分,避免移除了整数部分的尾部 0 ,可能还有其它边界情况没考虑,差不多这个意思。 |
![]() |
17
Chuckle 6 天前
适合数据可视化上数字展示
|
![]() |
19
dssxzuxc 5 天前 ![]() @UnluckyNinja #16
我以前项目也写过类似的东西 ```ts function n_str( value: number, options: { fractionDigits?: number } = {}, ): string { const { fractionDigits = 3 } = options const str = value.toFixed(fractionDigits).replace(/(\.?|(?<=\.\d+))0+$/, '') return str === '-0' ? '0' : str } ``` toFixed()的问题是无法智能判断精度,需要传入参数指定精度 nstr(1.123456) -> 1.123456 n_str(1.123456) -> 1.123 n_str(1.123456, { fractionDigits: 6 }) -> 1.123456 这里的难点是无法简单判断出浮点数计算的小数位数,比如 0.1+0.2 ,人类都知道应该是 1 位小数点,但是如何从 0.30000000000000004 解析出来,还有科学计数法把这个问题搞得更加复杂,而 nstr 能解决这个问题。手动指定精度 2 行就解决了,为了少写这个参数我不介意在项目里多 install 一个包。 目前 nstr 我测了所有情况,就剩 456.78999999456789=456.78 这个问题,其他的都正常 在 nstr 的方案上使用 toFixed 或许能解决这个问题同时不引入其他边缘情况,例如楼上的 pr https://github.com/shuding/nstr/pull/2/commits/4713d6447bc8fd3af2e246e63ea2f0edb8445d07 |
20
bli22ard 5 天前
这个库设计很有问题,0.1+0.2 已经为浮点数了,然后又转字符串,还能将 0.1+0.2 还原为 0.3 。如果你想表示 0.33999999999999998 ,它会给你转 0.34 ,结果就是 0.33999999999999998==0.34 这么做,明显有问题
|
![]() |
21
dssxzuxc 4 天前
@bli22ard #20
0.3399999999999999 = 0.34 是预期行为,这个库就是用来做这件事的。 这已经是第四遍回答了,当你需要显示/计算标准浮点/decimal 时,请使用原生 js 或者 decimal.js, bignumber.js 。 #16 已经说的很清楚了,这个库的作用是 `解决数字格式化的问题,避免因为精度误差导致显示的数字过长或过早使用科学计数法`。 设计上超过一定数量的连续 0 or 9 会直接截断,很显然这样才能实现 0.1 + 0.2 = 0.3 ,而带来的代价就是无法表达含有超过一定数量的连续 0 or 9 。 这个库的适用场景应该是大屏、统计页面等等各种数据可视化,四位以内小数运算是正确的,连续 0 or 9 超过一定次数就会自动四舍五入,严格计算老实去用 decimal.js, bignumber.js ,这只是解决了非严格 decimal 数据的展示问题,让编写显示浮点计算结果的代码更简单易懂适合人类阅读。 |
22
bli22ard 4 天前
@dssxzuxc #21 可能做法简洁,但是,我不认为是一个好方法,这样会让行为变得模糊起来。小数最佳实战应该是 decimal.js, bignumber.js 等计算完,然后格式化输出,而不是用这个格式化,造成 0.33999999999999998 看起来像 0.34 的不精确问题
|
![]() |
23
UnluckyNinja 4 天前 via Android
@bli22ard #22 怎么还没绕过这个弯,就不是精度的问题。会取近似值的,epsilon 都远远大于精度误差,根本没必要上高精度库,高精度库也解决不了显示问题。
就假设我有一个原始就是 0.3000004 的数(不是通过计算得来的,可能是网络,可能是可视化的数据集,总之这个数字本身就是这个形式),不需要计算,直接显示成人类可读字符串表示,这个情况你用高精度库有什么意义吗 |
24
bli22ard 4 天前
@UnluckyNinja #23 你的 0.3000004 ,不是计算得来,那是怎么得来的?是输入吗,为什么输入的不是 0.3 ,而输入 0.3000004 ?如果输入的是 0.3000004 ,显示成 0.3 是不是有问题?你的假设是不是有问题?
|
26
lvlongxiang199 4 天前
太投机取巧了, 输入一个大点的 int64 就精度丢失了 https://imgur.com/a/x6FFD5i
|
![]() |
27
dssxzuxc 4 天前
@635925926 #25 0.1.2 版本已经修复了这个问题了。
除此之外还有相关的 3 个 pr ,都是用的 toFixed ,简单易懂可以去除一堆边界条件,但是作者都没有接受,依旧采用土法硬算舍入值。 我对浮点计算没有深入研究,理论上 toFixed 处理 x.xxx9 和 x.xxx0 是完全正确的,而作者看起来似乎在硬扣性能,只有某些条件下才会回退到 toFixed 。 作者的代码实现还有个多余的 if (result === '0') { result = '0' } 完全没看懂在干啥 现在我是选了个其中一个 fork ,copy 一下放进 utils 里。 |
![]() |
28
UnluckyNinja 3 天前
@bli22ard #24 3000004/1000000 得来的可不可以?来自于网络的数据源很难理解吗?你管人家怎么来的,现在问题就是要展示这个数据,而你非要给计算上高精度库,关键是这个情景下就不涉及计算啊。
楼主热得快炸了,你就非得让楼主开空调? |
![]() |
29
UnluckyNinja 3 天前
@bli22ard #24 不过这么多人都看错,不怪你,得怪作者毫无必要地放了很多计算过程当作例子,网站文本存在误导,“修复浮点精度问题”但实际上只是显示上,而不是给出一个精确的计算结果。
然后 11 楼举了的例子也有一定误导,我用高精度库了更可能是因为我需要准确的结果,而不是为了数字转字符串看起来好看,这种情况下根本没有可比性。 这个库本身就是只有 number 类型到 string 类型转换,这么一个目的和功能。给定一个数字,返回一个字符串,就这么简单,甭管输入哪里来的,用户提供的,库作者想管也管不了。做 OJ 的时候也没人问 input 怎么来的吧。 不过本来我也不太看好这个库,为了这点功能徒增太多了复杂实现, 如果本身就要精度正确,那直接上精度库就好了, 如果要裁剪小数部分,那 toFixed 就可以了,去末尾 0 那就再加个正则替换。 如果为了智能判断高熵部分并展示……我不知道什么情况下会有这样一个需求,为什么要去在乎一个比 epsilon 小很多的噪音,就算如此,展示比 epsilon 小的值,为什么要做字符比较而不是基于数学方式去判断(例如 for 循环递增提取 5 位移到小数点后,tofixed(5)判断是否等于 1 或 0 ,而不是连续比较 5 个 0 或者 5 个 9 ) 我很怀疑原作者的精神状态…… |
30
bli22ard 3 天前
|
![]() |
31
UnluckyNinja 3 天前 via Android
@bli22ard #30 是的这些全是误导,他又不处理计算,都是 js 运行时自己计算,运算结果作为真正参数调用这个库函数,放这些算式纯纯误导。
他本身就是想解决“显示”有精度误差的数字的问题(作为其中一个功能但不是唯一功能),但正常人都会去想引入高精度库直接解决精度问题,而引入高精度库并不能解决“输入本身是一个有噪音部分小数的显示问题”,which 正是这个库“真正想解决的问题”(尽管他的实现让人感觉是不是被 AI 带坏了) 但愿你绕过来了 |