Cedric Blancher wrote:
> 
> Why do we need typeset -X? Can someone explain the difference between
> typeset -X and the existing typeset -E float format?

The difference is the ASCII representation. If you declare a variable
using typeset -E varname/typeset -F varname the ASCII representation
will use the base10. Since the internal floating-point representation
for a |long double| (e.g. IEEE754) uses base2 you get
base2--->base10--->base2 conversion/rounding errors if you convert a
variable to a string and back multiple times. ISO C99 solved this
problem by defining a new ASCII representation which uses a hexadecimal
notation for floating-point values which allows 100% accurate conversion
from the internal memory format into an ASCII string and back into the
machine's internal representation.

Example for the hexfloat format:
-- snip --
$ ksh93 -c 'float x=64.1 ; printf "%e\n%f\n%g\n%a\n" x x x
x'                                                      
6.410000e+01
64.100000
64.1
0x1.0066666666666666666666666666p+06
-- snip --
... e.g. "0x1.0066666666666666666666666666p+06" is the C99 hexadecimal
representation of the base10 value "64.1".

Example for hitting a rounding error:
-- snip --
alias hexfloat="typeset -lX"

set -o errexit

float    x
hexfloat y
float    res
typeset  str_x="" # string version of "x"
typeset  str_y="" # string version of "y"

for (( res=16. ; str_x == str_y ; res=res/2. )) ; do
        (( x=y=8.*res ))
        # convert x and y to their string representation
        str_x="$x"
        str_y="$y"
done
printf "%f != %f\n" "str_x" "str_y"
printf "%a != %a\n" "str_x" "str_y"
printf "res=%f\n" res

exit 0
-- snip --

The program does the following:
There are two variables "x" and "y" which hold an identical value in the
internal |long double| storage.
The value is then converted to a base10 representation for "x" (declared
as "float x") and this string stored in "str_x".
The same value is converted to a base16 (C99 hexfloat) representation
(declared as "typeset -lX") and this string stored in "str_y".
The "for(( ; ; ))" loop then cycles until the expression (( str_x ==
str_y )) returns "false" - which can only happen if the conversion of
the strings in "str_x" and "str_y" back to the internal |long double|
results in a difference.

On 64bit SPARC (where |long double| is 128bit) this application prints:
-- snip --
0.000030 != 0.000030
0x1.00000000b424dc35095cd80f5385p-15 !=
0x1.0000000000000000000000000000p-15
res=0.000001
-- snip --

Or short: $ typeset -X varname # (and/or $ printf "%a" varname #) is
usefull in cases where you need to "serialise" floating-point values
into strings but still need the accurate floating-point value when you
convert it back to an internal variable (the difference above looks tiny
but these tiny differences may ruin applications).

----

Bye,
Roland

-- 
  __ .  . __
 (o.\ \/ /.o) roland.mainz at nrubsig.org
  \__\/\/__/  MPEG specialist, C&&JAVA&&Sun&&Unix programmer
  /O /==\ O\  TEL +49 641 3992797
 (;O/ \/ \O;)

Reply via email to