mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 04:43:10 +00:00
✨ feat(math): add round options (#9117)
Add round options, but I think can also add floor, ceiling, etc. And the default mode is trunc. Closes #9117 Co-authored-by: Mahmoud Al-Qudsi <mqudsi@neosmart.net>
This commit is contained in:
parent
f1ae170155
commit
480d48351c
4 changed files with 101 additions and 9 deletions
|
@ -126,6 +126,7 @@ Scripting improvements
|
|||
- Commas in command substitution output are no longer used as separators in brace expansion, preventing a surprising expansion in rare cases (:issue:`5048`).
|
||||
- Universal variables can now store strings containing invalid Unicode codepoints (:issue:`10313`).
|
||||
- ``path basename`` now takes a ``-E`` option that causes it to return the basename (i.e. "filename" with the directory prefix removed) with the final extension (if any) also removed. This takes the place of ``path change-extension "" (path basename $foo)`` (:issue:`10521`).
|
||||
- ``math`` now adds ``--scale-mode`` parameter. You can choose between ``truncate``, ``round``, ``floor``, ``ceiling`` as you wish (default value is ``truncate``). (:issue:`9117`).
|
||||
|
||||
Interactive improvements
|
||||
------------------------
|
||||
|
|
|
@ -8,7 +8,7 @@ Synopsis
|
|||
|
||||
.. synopsis::
|
||||
|
||||
math [(-s | --scale) N] [(-b | --base) BASE] EXPRESSION ...
|
||||
math [(-s | --scale) N] [(-b | --base) BASE] [(-m | --scale-mode) MODE] EXPRESSION ...
|
||||
|
||||
|
||||
Description
|
||||
|
@ -19,7 +19,6 @@ It supports simple operations such as addition, subtraction, and so on, as well
|
|||
|
||||
By default, the output shows up to 6 decimal places.
|
||||
To change the number of decimal places, use the ``--scale`` option, including ``--scale=0`` for integer output.
|
||||
Trailing zeroes will always be trimmed.
|
||||
|
||||
Keep in mind that parameter expansion happens before expressions are evaluated.
|
||||
This can be very useful in order to perform calculations involving shell variables or the output of command substitutions, but it also means that parenthesis (``()``) and the asterisk (``*``) glob character have to be escaped or quoted.
|
||||
|
@ -37,8 +36,8 @@ The following options are available:
|
|||
**-s** *N* or **--scale** *N*
|
||||
Sets the scale of the result.
|
||||
``N`` must be an integer or the word "max" for the maximum scale.
|
||||
A scale of zero causes results to be truncated, not rounded. Any non-integer component is thrown away.
|
||||
So ``3/2`` returns ``1`` rather than ``2`` which ``1.5`` would normally round to.
|
||||
A scale of zero causes results to be truncated by default. Any non-integer component is thrown away.
|
||||
So ``3/2`` returns ``1`` by default, rather than ``2`` which ``1.5`` would normally round to.
|
||||
This is for compatibility with ``bc`` which was the basis for this command prior to fish 3.0.0.
|
||||
Scale values greater than zero causes the result to be rounded using the usual rules to the specified number of decimal places.
|
||||
|
||||
|
@ -49,6 +48,11 @@ The following options are available:
|
|||
Hex numbers will be printed with a ``0x`` prefix.
|
||||
Octal numbers will have a prefix of ``0`` but aren't understood by ``math`` as input.
|
||||
|
||||
**-m** *MODE* or **--scale-mode** *MODE*
|
||||
Sets scale behavior.
|
||||
The ``MODE`` can be ``truncate``, ``round``, ``floor``, ``ceiling``.
|
||||
The default value of scale mode is ``round`` with non zero scale and ``truncate`` with zero scale.
|
||||
|
||||
**-h** or **--help**
|
||||
Displays help about using this command.
|
||||
|
||||
|
|
|
@ -1,17 +1,31 @@
|
|||
use num_traits::pow;
|
||||
use widestring::utf32str;
|
||||
|
||||
use super::prelude::*;
|
||||
use crate::tinyexpr::te_interp;
|
||||
|
||||
/// The maximum number of points after the decimal that we'll print.
|
||||
const DEFAULT_SCALE: usize = 6;
|
||||
|
||||
const DEFAULT_ZERO_SCALE_MODE: ZeroScaleMode = ZeroScaleMode::Default;
|
||||
|
||||
/// The end of the range such that every integer is representable as a double.
|
||||
/// i.e. this is the first value such that x + 1 == x (or == x + 2, depending on rounding mode).
|
||||
const MAX_CONTIGUOUS_INTEGER: f64 = (1_u64 << f64::MANTISSA_DIGITS) as f64;
|
||||
|
||||
enum ZeroScaleMode {
|
||||
Truncate,
|
||||
Round,
|
||||
Floor,
|
||||
Ceiling,
|
||||
Default,
|
||||
}
|
||||
|
||||
struct Options {
|
||||
print_help: bool,
|
||||
scale: usize,
|
||||
base: usize,
|
||||
zero_scale_mode: ZeroScaleMode,
|
||||
}
|
||||
|
||||
fn parse_cmd_opts(
|
||||
|
@ -24,17 +38,19 @@ fn parse_cmd_opts(
|
|||
|
||||
// This command is atypical in using the "+" (REQUIRE_ORDER) option for flag parsing.
|
||||
// This is needed because of the minus, `-`, operator in math expressions.
|
||||
const SHORT_OPTS: &wstr = L!("+:hs:b:");
|
||||
const SHORT_OPTS: &wstr = L!("+:hs:b:m:");
|
||||
const LONG_OPTS: &[WOption] = &[
|
||||
wopt(L!("scale"), ArgType::RequiredArgument, 's'),
|
||||
wopt(L!("base"), ArgType::RequiredArgument, 'b'),
|
||||
wopt(L!("help"), ArgType::NoArgument, 'h'),
|
||||
wopt(L!("scale-mode"), ArgType::RequiredArgument, 'm'),
|
||||
];
|
||||
|
||||
let mut opts = Options {
|
||||
print_help: false,
|
||||
scale: DEFAULT_SCALE,
|
||||
base: 10,
|
||||
zero_scale_mode: DEFAULT_ZERO_SCALE_MODE,
|
||||
};
|
||||
|
||||
let mut have_scale = false;
|
||||
|
@ -62,6 +78,23 @@ fn parse_cmd_opts(
|
|||
opts.scale = scale as usize;
|
||||
}
|
||||
}
|
||||
'm' => {
|
||||
let optarg = w.woptarg.unwrap();
|
||||
if optarg.eq(utf32str!("truncate")) {
|
||||
opts.zero_scale_mode = ZeroScaleMode::Truncate;
|
||||
} else if optarg.eq(utf32str!("round")) {
|
||||
opts.zero_scale_mode = ZeroScaleMode::Round;
|
||||
} else if optarg.eq(utf32str!("floor")) {
|
||||
opts.zero_scale_mode = ZeroScaleMode::Floor;
|
||||
} else if optarg.eq(utf32str!("ceiling")) {
|
||||
opts.zero_scale_mode = ZeroScaleMode::Ceiling;
|
||||
} else {
|
||||
streams
|
||||
.err
|
||||
.append(wgettext_fmt!("%ls: %ls: invalid mode\n", cmd, optarg));
|
||||
return Err(STATUS_INVALID_ARGS);
|
||||
}
|
||||
}
|
||||
'b' => {
|
||||
let optarg = w.woptarg.unwrap();
|
||||
if optarg == "hex" {
|
||||
|
@ -129,10 +162,26 @@ fn format_double(mut v: f64, opts: &Options) -> WString {
|
|||
return sprintf!("%s0%lo", mneg, v.abs() as u64);
|
||||
}
|
||||
|
||||
// As a special-case, a scale of 0 means to truncate to an integer
|
||||
// instead of rounding.
|
||||
if opts.scale == 0 {
|
||||
v = v.trunc();
|
||||
v *= pow(10f64, opts.scale);
|
||||
|
||||
v = match opts.zero_scale_mode {
|
||||
ZeroScaleMode::Truncate => v.trunc(),
|
||||
ZeroScaleMode::Round => v.round(),
|
||||
ZeroScaleMode::Floor => v.floor(),
|
||||
ZeroScaleMode::Ceiling => v.ceil(),
|
||||
ZeroScaleMode::Default => {
|
||||
if opts.scale == 0 {
|
||||
v.trunc()
|
||||
} else {
|
||||
v
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// if we don't add check here, the result of 'math -s 0 "22 / 5 - 5"' will be '0', not '-0'
|
||||
if opts.scale != 0 {
|
||||
v /= pow(10f64, opts.scale);
|
||||
} else {
|
||||
return sprintf!("%.*f", opts.scale, v);
|
||||
}
|
||||
|
||||
|
|
|
@ -379,3 +379,41 @@ math 0x0_2.0P-f
|
|||
# CHECKERR: math: Error: Unexpected token
|
||||
# CHECKERR: '0x0_2.0P-f'
|
||||
# CHECKERR: ^
|
||||
math "22 / 5 - 5"
|
||||
# CHECK: -0.6
|
||||
math -s 0 --scale-mode=truncate "22 / 5 - 5"
|
||||
# CHECK: -0
|
||||
math --scale=0 -m truncate "22 / 5 - 5"
|
||||
# CHECK: -0
|
||||
math -s 0 --scale-mode=floor "22 / 5 - 5"
|
||||
# CHECK: -1
|
||||
math -s 0 --scale-mode=round "22 / 5 - 5"
|
||||
# CHECK: -1
|
||||
math -s 0 --scale-mode=ceiling "22 / 5 - 5"
|
||||
# CHECK: -0
|
||||
math "1 / 3 - 1"
|
||||
# CHECK: -0.666667
|
||||
math --scale-mode=truncate "1 / 3 - 1"
|
||||
# CHECK: -0.666666
|
||||
math --scale-mode=floor "1 / 3 - 1"
|
||||
# CHECK: {{-0.666667|-0.666668}}
|
||||
math --scale-mode=floor "2 / 3 - 1"
|
||||
# CHECK: {{-0.333334|-0.333335}}
|
||||
math --scale-mode=round "1 / 3 - 1"
|
||||
# CHECK: {{-0.666667|-0.666668}}
|
||||
math --scale-mode=ceiling "1 / 3 - 1"
|
||||
# CHECK: -0.666666
|
||||
math --scale-mode=ceiling "2 / 3 - 1"
|
||||
# CHECK: -0.333333
|
||||
math -s 6 --scale-mode=truncate "1 / 3 - 1"
|
||||
# CHECK: -0.666666
|
||||
math -s 6 --scale-mode=floor "1 / 3 - 1"
|
||||
# CHECK: {{-0.666667|-0.666668}}
|
||||
math -s 6 --scale-mode=floor "2 / 3 - 1"
|
||||
# CHECK: {{-0.333334|-0.333335}}
|
||||
math -s 6 --scale-mode=round "1 / 3 - 1"
|
||||
# CHECK: {{-0.666667|-0.666668}}
|
||||
math -s 6 --scale-mode=ceiling "1 / 3 - 1"
|
||||
# CHECK: -0.666666
|
||||
math -s 6 --scale-mode=ceiling "2 / 3 - 1"
|
||||
# CHECK: -0.333333
|
||||
|
|
Loading…
Reference in a new issue