1
Fork 0
mirror of https://github.com/RGBCube/nu_scripts synced 2025-08-01 06:37:46 +00:00

update significant-digits to not use string conversions (#862)

Thanks to kfarmer (a
[conversation](https://discord.com/channels/601130461678272522/615253963645911060/1247031648965492766)
on discord) for proposing his version and motivating me for this update
This commit is contained in:
Maxim Uvarov 2024-06-03 20:06:51 +08:00 committed by GitHub
parent 1dbc16cfb7
commit ae5c1a2727
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,6 +1,3 @@
use std iter scan
# replace all insignificant digits with 0 # replace all insignificant digits with 0
# #
# | Significant Digits | Maximum Relative Error | # | Significant Digits | Maximum Relative Error |
@ -18,29 +15,50 @@ use std iter scan
# > 1.2346789 | significant-digits 3 # > 1.2346789 | significant-digits 3
# 1.23 # 1.23
# #
# > 123456789.89 | significant-digits 5
# 123450000
#
# > 1sec / 3 | math significant-digits # > 1sec / 3 | math significant-digits
# 333ms # 333ms
export def 'significant-digits' [ export def 'significant-digits' [
n: int = 4 # a number of first non-zero digits to keep n: int = 3 # a number of significant digits
]: [int -> int, float -> float, duration -> duration] { ]: [int -> int, float -> float, duration -> duration] {
let $input = $in let $input = $in
let $chars = $input | into string | split chars let $type = $input | describe
let $rounded_str = $chars let $num = match $type {
| scan --noinit 0 {|ind i| 'duration' => {$input | into int}
if $i =~ '\d' { _ => {$input}
if $ind == 0 and $i == '0' { }
$ind
} else { $ind + 1 } let insignif_position = $n - 1 - ($num | math abs | math log 10 | math floor)
} else {$ind}
# See the note below the code for an explanation of the construct used.
let scaling_factor = 10 ** ($insignif_position | math abs)
let res = $num
| if $insignif_position > 0 {
$in * $scaling_factor
} else {
$in / $scaling_factor
}
| math floor
| if $insignif_position <= 0 {
$in * $scaling_factor
} else {
$in / $scaling_factor
} }
| zip $chars
| each {|i| if $i.1 =~ '\d' and $i.0 > $n {'0'} else {$i.1}}
| str join
match ($input | describe) { match $type {
'duration' => {$rounded_str | into duration} 'duration' => {$res | into duration}
'int' => {$rounded_str | into int} 'int' => {$res | into int}
'float' => {$rounded_str | into float} _ => {$res}
} }
} }
# I started with `10.0 ** $insignif_position`, but it was sometimes producing
# not rounded digits in `$num / $scaling_factor` if `$insignif_position` was negative
# like with
# > 3456789 | math round --precision -5
# 3499999.9999999995
# so I use what I have now.