zsh 在对两个大小明显不同的浮点数进行乘法运算时会失去精度

zsh loses precision when miltiplying two floats of significantly different magnitude

我正在尝试在 zsh 脚本中进行一些浮点运算。我看到以下行为:

$ (( a = 1.23456789 * 0.00000001 )); printf "a = %g\n" $a
a = 1.23e-08

$ (( a = 1.23456789 * 0.00000001 )); printf "a = %e\n" $a
a = 1.230000e-08

$ (( a = 1.23456789 * 0.0000001 )); printf "a = %e\n" $a
a = 1.235000e-07

当我仅将第一个数字乘以一个尾数为 1(或至少非常接近 1,如果考虑真正的二进制表示)的数字时,我希望不会丢失第一个数字的尾数精度。换句话说,我希望得到 a = 1.23456789e-08 或一些截断的尾数,但在 1.23 / 1.235.

之后不是零

我是运行以下版本:

$ zsh --version
zsh 5.8 (x86_64-apple-darwin20.0)

我错过了什么吗?还是 zsh 的问题?我是 zsh 的新手,一般来说我在 shell 编程方面没有太多经验,因此非常感谢任何帮助。谢谢!

似乎 (( x = 1.0 )),当 x 未定义时,将导致 Zsh 将变量声明为 -F:双精度浮点数,格式化为具有 10 个十进制数字的定点数输出:

% unset x; (( x = 0.12345678901234567 )); declare -p x
typeset -F x=0.1234567890

% unset x; x=$((0.12345678901234567)); declare -p x
typeset x=0.12345678901234566

我不知道为什么会这样,但如果你先手动将你的变量声明为字符串,就不会发生这种情况,你会得到完整的值:

% unset a; typeset a; (( a = 1.23456789 * 0.00000001 )); printf "a = %g\n" $a
a = 1.23457e-08

区别在于将 a 的值传递给 printf 的方式。如果写成

(( a = 1.23456789 * 0.00000001 )); printf "a = %e\n" $((a))

$ (( a = 1.23456789 * 0.0000001 )); printf "a = %e\n" $((a))

没有出现该问题。这在 here 中被描述为:

floating point numbers can be declared with the float builtin; there are two types, differing only in their output format, as described for the typeset builtin. The output format can be bypassed by using arithmetic substitution instead of the parameter substitution, i.e. ‘${float}’ uses the defined format, but ‘$((float))’ uses a generic floating point format