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
#
# | Significant Digits | Maximum Relative Error |
@ -18,29 +15,50 @@ use std iter scan
# > 1.2346789 | significant-digits 3
# 1.23
#
# > 123456789.89 | significant-digits 5
# 123450000
#
# > 1sec / 3 | math significant-digits
# 333ms
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] {
let $input = $in
let $chars = $input | into string | split chars
let $type = $input | describe
let $rounded_str = $chars
| scan --noinit 0 {|ind i|
if $i =~ '\d' {
if $ind == 0 and $i == '0' {
$ind
} else { $ind + 1 }
} else {$ind}
let $num = match $type {
'duration' => {$input | into int}
_ => {$input}
}
| zip $chars
| each {|i| if $i.1 =~ '\d' and $i.0 > $n {'0'} else {$i.1}}
| str join
match ($input | describe) {
'duration' => {$rounded_str | into duration}
'int' => {$rounded_str | into int}
'float' => {$rounded_str | into float}
let insignif_position = $n - 1 - ($num | math abs | math log 10 | math floor)
# 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
}
match $type {
'duration' => {$res | into duration}
'int' => {$res | into int}
_ => {$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.