我手上有点神秘。我找到了一个子程序(http://macscripter.net/viewtopic.php?id=27520)来将一个科学数字转换成一串数字。但是,无论我尝试什么,它似乎都会删除剩余的数字。
“1.23456789E + 4”应变为“12345.6789”。相反,它只返回“12345”。
尝试运行以下代码,您将看到我的意思。我打电话给对话框披露结果:
set xx to 1.23456789E+4
set yy to number_to_string(xx)
display dialog yy
on number_to_string(this_number)
set this_number to this_number as string
set deci to character 2 of (0.5 as text)
set x to the offset of deci in this_number
set z to the offset of "E" in this_number
if this_number contains "E+" then
set y to the offset of "+" in this_number
set the decimal_adjust to characters (y - (length of this_number)) thru ¬
-1 of this_number as string as number
if x is not 0 then
set the first_part to characters 1 thru (x - 1) of this_number as string
else
set the first_part to ""
end if
set the second_part to characters (x + 1) thru (z - 1) of this_number as string
set the converted_number to the first_part
repeat with i from 1 to the decimal_adjust
try
set the converted_number to ¬
the converted_number & character i of the second_part
on error
set the converted_number to the converted_number & "0"
end try
end repeat
return the converted_number
else
if this_number contains "E-" then
set y to the offset of "-" in this_number
if x is not 0 then
set the first_part to text 1 thru (x - 1) of this_number
else
set the first_part to ""
end if
set the second_part to text (x + 1) thru (z - 1) of this_number
set the converted_number to the first_part & second_part
set n to text (y + 1) thru -1 of this_number as number
set zero to "0."
if n > 1 then
repeat (n - 1) times
set zero to zero & "0"
end repeat
end if
set converted_number to zero & converted_number
else
set converted_number to this_number
end if
end if
return converted_number
end number_to_string
答案 0 :(得分:2)
至于为什么你的AppleScript代码不起作用:
您的代码只关注 exponent ,而不关注尾数 中的位数(数字的小数部分)在指数之前)。
因此,以1.23456789E+4
作为输入,提取尾数的严格 4 数字以形成结果,而不管尾数有多少位数:1
& ; 2345678
的前4位数,产生12345
。
在AppleScript中实现这一点非常重要,所以我建议使用do shell script
使用bc
, a POSIX arbitrary-precision calculation utility 的shell命令,这样可以更快地到达目的地:
set xx to "1.23456789E+4" # define as *string* to avoid rounding errors
# Perform transformation via handler defined below.
set yy to my toDecFraction(xx)
display alert yy
on toDecFraction(numStr)
local maxDecPlaces
# For *negative* exponents: set the maximum number of decimal places in the result.
# For *positive* exponents: the number of decimal places in the result is
# automatically chosen to accommodate all digits.
# In either case: the max. number of decimal places supported is 2,147,483,647.
set maxDecPlaces to 32
do shell script "{ printf 'scale=" & maxDecPlaces & ¬
"; '; sed -E 's/[eE]\\+?/*10^/g' <<<" & quoted form of (numStr as text) & "; } |
bc | tr -d '\\n\\' |
sed -E -e '/\\./!b' -e 's/\\.0+$//;t' -e 's/0+$//; s/^(-?)(\\.)/\\10\\2/'"
end toDecFraction
printf 'scale=<n>;'
,当发送到bc
时,指示在否定指数的情况下,它使用<n>
小数位的精度;如果指数正,bc
会自动选择保留所有数字的精度。
2,147,483,647
(!)(2^32/2-1
),但请注意,您为maxDecPlaces
选择的数字越大(如果是一个负指数)或你输入的小数位数(如果是正指数),转换所需的时间越长,但实际上在32之间的限制之间的性能差别不大。 200(!)小数位。请注意,如果限制太低,则会发生截断,而不是舍入。sed -E 's/[eE]\+?/*10^/g''
将科学记数法重新格式化为bc
可以评估的完全等效的算术表达式;例如。:
1e2
- &gt; 1*10^2
.3e+1
- &gt; .3*10^1
2.5e-2
- &gt; 2.5*10^-2
bc
只需将结果打印为小数,其输入所隐含的小数位数(如果为正指数),或者通过指定的变量scale
(如果是负指数)tr -d '\n\'
字符需要\
。输出超过70个字符的数字时bc
插入的换行符。sed -E -e '/\\./!b' -e 's/\\.0+$//;t' -e 's/0+$//; s/^(-?)(\\.)/\\10\\2/'
清除结果会从结果中删除尾随零(如果没有剩下小数位,也会删除小数点),如果结果的(绝对值),则前置0
是&lt; 1。注意强>:
0
,则 打印,因此,例如1e-2
打印为{{ 1}},在AppleScript中是正常的 - 而不是0.01
。
.01
,请使用0
替换上面代码中的-e 's/0+$//; s/^(-?)(\\.)/\\10\\2/'
。-e 's/0+$//'
是设计不区域设置感知,所以基数字符(&#34;小数点&#34;)是预期的输入和产出上的产品总是 bc
为了进行比较,这里有一个处理程序,它使用纯.
代码来执行转换词法 - 正如您所看到的,滚动一个&#的努力39;自己的转变是非常重要的 - 在AppleScript中会更加冗长。
在实践中,两种方法的表现基本相同。 此解决方案的优点是,小数位数没有限制,所有数字都会自动保留,无法识别的数字字符串可靠地引发错误。
bash
这里是嵌入式set xx to "1.23456789E+4" # define as *string* to avoid rounding errors
# Perform transformation via handler defined below.
set yy to my toDecFraction(xx)
display alert yy
# SYNOPSIS
# toDecFraction(numString)
# DESCRIPTION
# Textually reformats the specified number string from decimal exponential (scientific) notation
# (e.g., 1.234e+2) to a decimal fraction (e.g., 123.4).
# Leading and trailing whitespace is acceptable.
# Input that is in integer form or already a decimal fraction is accepted, and echoed *unmodified*.
# No fractional part is output if there is none; e.g., '1.2e1' results in '12'.
# Numbers with an integer part of 0 are output with the leading zero (e.g. '0.1', not '.1')
# Unrecognized number strings result in an error.
# There is no limit on the number of decimal places and there are no rounding errors, given that
# the transformation is purely *lexical*.
# NOTE: This function is NOT locale-aware: a '.' must always be used as the radix character.
# EXAMPLES
# my toDecFraction('1.234567e+2') # -> '123.4567'
# my toDecFraction(toDecFraction '+1e-3') # -> '0.001'
# my toDecFraction('-1.23e+3') # -> '-1230'
# my toDecFraction ('1e-1') # -> '0.01'
on toDecFraction(numStr)
try
do shell script "
toDecFraction() {
local numStr leadingZero sign intPart fractPart expSign exponent allDigits intDigitCount intDigits fractDigits padCount result
{ [[ $1 == '--' ]] && shift; } || { [[ $1 == '-z' ]] && { leadingZero=1; shift; } }
read -r numStr <<<\"$1\" # trim leading and trailing whitespace
# Parse into constituent parts and fail, if not recognized as decimal integer / exponential notation.
[[ $numStr =~ ^([+-]?)([[:digit:]]+)?\\.?(([[:digit:]]+)?([eE]([+-]?)([[:digit:]]+))?)?$ ]] || return 1
sign=${BASH_REMATCH[1]} intPart=${BASH_REMATCH[2]}
fractPart=${BASH_REMATCH[4]} expSign=${BASH_REMATCH[6]} exponent=${BASH_REMATCH[7]}
# If there's neither an integer nor a fractional part, fail.
[[ -n $intPart || -n $fractPart ]] || return 1
# debugging: echo \"[$sign][$intPart].[$fractPart]e[$expSign][$exponent]\"
# If there's no exponent involved, output the number as is
# (It is either an integer or already a decimal fraction.)
[[ -n $exponent ]] || { echo \"$1\"; return 0; }
allDigits=${intPart}${fractPart}
# Calculate the number of integer digits in the resulting decimal fraction,
# after resolving the exponent.
intDigitCount=$(( ${#intPart} + ${expSign}${exponent} ))
# If the sign was an explicit +, set it to the empty string - we don't want to output it.
[[ $sign == '+' ]] && sign=''
if (( intDigitCount > 0 )); then # at least 1 integer digit
intDigits=${allDigits:0:intDigitCount}
padCount=$(( intDigitCount - ${#intDigits} ))
(( padCount > 0 )) && intDigits=${intDigits}$(printf \"%${padCount}s\" | tr ' ' '0')
fractDigits=${allDigits:intDigitCount} # determine what goes after the radix character
result=${sign}${intDigits}${fractDigits:+.}${fractDigits}
# Remove leading zeros, if any.
[[ $result =~ ^0+([^0].*)?$ ]] && result=\"${BASH_REMATCH[1]}\"
else # result is < 1
padCount=$(( -intDigitCount ))
result=${sign}${leadingZero:+0}.$(printf \"%${padCount}s\" | tr ' ' '0')${intPart}${fractPart}
fi
# Trim an empty fractional part, and ensure that if
# the result is empty, '0' is output.
[[ $result =~ ^([^.]*)\\.0+$ ]] && result=\"${BASH_REMATCH[1]}\"
printf '%s\\n' \"${result:-0}\"
}
toDecFraction -z " & quoted form of (numStr as text)
on error number errNum
error "Not recognized as a number: " & (numStr as text) number (500 + errNum)
end try
end toDecFraction
功能,并带有正确的语法高亮显示:
bash
最后,这是一个更简单的shell命令,然而,不推荐,因为它受到双重的固有舍入误差的影响精确浮点值,所以你不能保证所有数字都被(忠实地)保存。:
toDecFraction() {
local numStr leadingZero sign intPart fractPart expSign exponent allDigits intDigitCount intDigits fractDigits padCount result
{ [[ $1 == '--' ]] && shift; } || { [[ $1 == '-z' ]] && { leadingZero=1; shift; } }
read -r numStr <<<"$1" # trim leading and trailing whitespace
# Parse into constituent parts and fail, if not recognized as decimal integer / exponential notation.
[[ $numStr =~ ^([+-]?)([[:digit:]]+)?\.?(([[:digit:]]+)?([eE]([+-]?)([[:digit:]]+))?)?$ ]] || return 1
sign=${BASH_REMATCH[1]} intPart=${BASH_REMATCH[2]}
fractPart=${BASH_REMATCH[4]} expSign=${BASH_REMATCH[6]} exponent=${BASH_REMATCH[7]}
# If there's neither an integer nor a fractional part, fail.
[[ -n $intPart || -n $fractPart ]] || return 1
# debugging: echo "[$sign][$intPart].[$fractPart]e[$expSign][$exponent]"
# If there's no exponent involved, output the number as is
# (It is either an integer or already a decimal fraction.)
[[ -n $exponent ]] || { echo "$1"; return 0; }
allDigits=${intPart}${fractPart}
# Calculate the number of integer digits in the resulting decimal fraction,
# after resolving the exponent.
intDigitCount=$(( ${#intPart} + ${expSign}${exponent} ))
# If the sign was an explicit +, set it to the empty string - we don't want to output it.
[[ $sign == '+' ]] && sign=''
if (( intDigitCount > 0 )); then # at least 1 integer digit
intDigits=${allDigits:0:intDigitCount}
padCount=$(( intDigitCount - ${#intDigits} ))
(( padCount > 0 )) && intDigits=${intDigits}$(printf "%${padCount}s" | tr ' ' '0')
fractDigits=${allDigits:intDigitCount} # determine what goes after the radix character
result=${sign}${intDigits}${fractDigits:+.}${fractDigits}
# Remove leading zeros, if any.
[[ $result =~ ^0+([^0].*)?$ ]] && result="${BASH_REMATCH[1]}"
else # result is < 1
padCount=$(( -intDigitCount ))
result=${sign}${leadingZero:+0}.$(printf "%${padCount}s" | tr ' ' '0')${intPart}${fractPart}
fi
# Trim an empty fractional part, and ensure that if
# the result is empty, '0' is output.
[[ $result =~ ^([^.]*)\.0+$ ]] && result="${BASH_REMATCH[1]}"
printf '%s\n' "${result:-0}"
}
该命令使用set xx to "1.23456789E+4"
set yy to do shell script "awk -v n=" & quoted form of (xx as text) & " 'BEGIN \\
{ CONVFMT=\"%.11f\"; ns=\"\"(n + 0); if (ns ~ /\\./) gsub(\"0+$\",\"\",ns); print ns }'"
display alert yy
的原生能力来识别科学记数法,并使用(隐式应用)awk
数字格式printf
将结果数字转换回字符串 - 即小数点后11位;在返回结果之前,会修剪任何尾随零(使用"%.11f"
)。
乍一看,这个似乎没问题:结果是gsub()
。
但是,如果您将小数位数更改为 12 (12345.6789
),则会出现舍入错误:CONVFMT=\"%.12f\"
(!)
如果发生这种情况,您事先不会知道,所以如果需要忠实保存所有数字,这种做法是不可行的。
答案 1 :(得分:-1)
快速谷歌搜索出现了this,但正如您所指出的那样,基本代码与您无关。为了弥补我的愚蠢错误,我写了这个AppleScript来完成这项工作。它适用于正/负指数和正/负数。祝你好运。
numberToString(-1.23456789E+4)
on numberToString(aNumber)
set aNumber to aNumber as text
-- check for a negative number
set isNegative to false
if character 1 of aNumber is "-" then
set isNegative to true
set aNumber to text 2 thru -1 of aNumber
end if
try
set a to the offset of "." in aNumber
set b to the offset of "E" in aNumber
set c to the offset of "+" in aNumber
set d to the offset of "-" in aNumber
if b is 0 then -- we do not have an exponential number
if isNegative then
return "-" & aNumber
else
return aNumber
end if
end if
if a is 0 then
set firstPart to ""
else
set firstPart to text 1 thru (a - 1) of aNumber
end if
set secondPart to text (a + 1) thru (b - 1) of aNumber
if c is 0 and d is 0 then -- assume a positive exponent
set isPositiveExponent to true
set thirdPart to text (b + 1) thru -1 of aNumber
else if c is not 0 then
set isPositiveExponent to true
set thirdPart to text (b + 2) thru -1 of aNumber
else
set isPositiveExponent to false
set thirdPart to text (b + 2) thru -1 of aNumber
end if
set thirdPart to thirdPart as number
if isPositiveExponent then
set newNumber to firstPart
set theRemainder to secondPart
repeat with i from 1 to thirdPart
try
set newNumber to newNumber & character i of secondPart
if theRemainder is not "" then
if (count of theRemainder) is 1 then
set theRemainder to ""
else
set theRemainder to text 2 thru -1 of theRemainder
end if
end if
on error
set newNumber to newNumber & "0"
end try
end repeat
if theRemainder is not "" then
set newNumber to newNumber & "." & theRemainder
end if
else
set newNumber to ""
set theRemainder to firstPart
repeat with i from 1 to thirdPart
try
set newNumber to character -i of firstPart & newNumber
if theRemainder is not "" then
if (count of theRemainder) is 1 then
set theRemainder to ""
else
set theRemainder to text 1 thru -2 of theRemainder
end if
end if
on error
set newNumber to "0" & newNumber
end try
end repeat
if theRemainder is not "" then
set newNumber to theRemainder & "." & newNumber & secondPart
else
set newNumber to "0." & newNumber & secondPart
end if
end if
on error
if isNegative then
return "-" & aNumber
else
return aNumber
end if
end try
if isNegative then
return "-" & newNumber
else
return newNumber
end if
end numberToString