mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-22 09:28:31 +08: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
|
@ -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`).
|
- 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`).
|
- 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`).
|
- ``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
|
Interactive improvements
|
||||||
------------------------
|
------------------------
|
||||||
|
|
|
@ -8,7 +8,7 @@ Synopsis
|
||||||
|
|
||||||
.. synopsis::
|
.. synopsis::
|
||||||
|
|
||||||
math [(-s | --scale) N] [(-b | --base) BASE] EXPRESSION ...
|
math [(-s | --scale) N] [(-b | --base) BASE] [(-m | --scale-mode) MODE] EXPRESSION ...
|
||||||
|
|
||||||
|
|
||||||
Description
|
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.
|
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.
|
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.
|
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.
|
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*
|
**-s** *N* or **--scale** *N*
|
||||||
Sets the scale of the result.
|
Sets the scale of the result.
|
||||||
``N`` must be an integer or the word "max" for the maximum scale.
|
``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.
|
A scale of zero causes results to be truncated by default. Any non-integer component is thrown away.
|
||||||
So ``3/2`` returns ``1`` rather than ``2`` which ``1.5`` would normally round to.
|
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.
|
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.
|
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.
|
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.
|
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**
|
**-h** or **--help**
|
||||||
Displays help about using this command.
|
Displays help about using this command.
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,31 @@
|
||||||
|
use num_traits::pow;
|
||||||
|
use widestring::utf32str;
|
||||||
|
|
||||||
use super::prelude::*;
|
use super::prelude::*;
|
||||||
use crate::tinyexpr::te_interp;
|
use crate::tinyexpr::te_interp;
|
||||||
|
|
||||||
/// The maximum number of points after the decimal that we'll print.
|
/// The maximum number of points after the decimal that we'll print.
|
||||||
const DEFAULT_SCALE: usize = 6;
|
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.
|
/// 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).
|
/// 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;
|
const MAX_CONTIGUOUS_INTEGER: f64 = (1_u64 << f64::MANTISSA_DIGITS) as f64;
|
||||||
|
|
||||||
|
enum ZeroScaleMode {
|
||||||
|
Truncate,
|
||||||
|
Round,
|
||||||
|
Floor,
|
||||||
|
Ceiling,
|
||||||
|
Default,
|
||||||
|
}
|
||||||
|
|
||||||
struct Options {
|
struct Options {
|
||||||
print_help: bool,
|
print_help: bool,
|
||||||
scale: usize,
|
scale: usize,
|
||||||
base: usize,
|
base: usize,
|
||||||
|
zero_scale_mode: ZeroScaleMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_cmd_opts(
|
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 command is atypical in using the "+" (REQUIRE_ORDER) option for flag parsing.
|
||||||
// This is needed because of the minus, `-`, operator in math expressions.
|
// 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] = &[
|
const LONG_OPTS: &[WOption] = &[
|
||||||
wopt(L!("scale"), ArgType::RequiredArgument, 's'),
|
wopt(L!("scale"), ArgType::RequiredArgument, 's'),
|
||||||
wopt(L!("base"), ArgType::RequiredArgument, 'b'),
|
wopt(L!("base"), ArgType::RequiredArgument, 'b'),
|
||||||
wopt(L!("help"), ArgType::NoArgument, 'h'),
|
wopt(L!("help"), ArgType::NoArgument, 'h'),
|
||||||
|
wopt(L!("scale-mode"), ArgType::RequiredArgument, 'm'),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut opts = Options {
|
let mut opts = Options {
|
||||||
print_help: false,
|
print_help: false,
|
||||||
scale: DEFAULT_SCALE,
|
scale: DEFAULT_SCALE,
|
||||||
base: 10,
|
base: 10,
|
||||||
|
zero_scale_mode: DEFAULT_ZERO_SCALE_MODE,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut have_scale = false;
|
let mut have_scale = false;
|
||||||
|
@ -62,6 +78,23 @@ fn parse_cmd_opts(
|
||||||
opts.scale = scale as usize;
|
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' => {
|
'b' => {
|
||||||
let optarg = w.woptarg.unwrap();
|
let optarg = w.woptarg.unwrap();
|
||||||
if optarg == "hex" {
|
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);
|
return sprintf!("%s0%lo", mneg, v.abs() as u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
// As a special-case, a scale of 0 means to truncate to an integer
|
v *= pow(10f64, opts.scale);
|
||||||
// instead of rounding.
|
|
||||||
if opts.scale == 0 {
|
v = match opts.zero_scale_mode {
|
||||||
v = v.trunc();
|
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);
|
return sprintf!("%.*f", opts.scale, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -379,3 +379,41 @@ math 0x0_2.0P-f
|
||||||
# CHECKERR: math: Error: Unexpected token
|
# CHECKERR: math: Error: Unexpected token
|
||||||
# CHECKERR: '0x0_2.0P-f'
|
# CHECKERR: '0x0_2.0P-f'
|
||||||
# CHECKERR: ^
|
# 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 New Issue
Block a user