Discussion:
[ast-developers] [prototype-patch] Prototype for correct { |float|, |double|, |long double| }-handing of |nextafter()| ... / was: Re: [ast-users] Unit of least precision (ULP)/spacing between floating point number problems
Roland Mainz
2013-08-11 22:14:17 UTC
Permalink
On Sun, Aug 11, 2013 at 6:15 PM, Cedric Blancher
[snip]
But why does nextafter() misbehave if I want to use a datatype smaller
than "long double"? Accuracy is a good thing, but in this case we
iterate too fine-grained, meaning the code should iterate over the
smallest possible steps of a double, but not over the smallest
possible steps of a long double.
Does anyone have a good idea how to fix this in ksh?
Grumpf... yes. Technically I feared that day may come when
|nextafter()| and |nexttoward()| were added in ksh93... ;-/
The issue is more or less like this: Both |nextafter(f|l|)\(\)| and
|nexttoward(f|l|)\(\)| step over the smallest possible quantity for
the specific { |float|, |double|, |long double| }-datatype and
therefore (for example) using |nextafterl()| (intended for |long
double|) for a |float| doesn't work because it does so small steps
that they cannot be represented in a |float| ... that causes the
endless loop in Tina's example.
The fix would be to "remember" the datatype (e.g. { |float|,
|double|, |long double| }) for a given variable and pass that down to
|arith_exec()| and call the specific version of |nextafter()| and
- variables declared via typeset -s -E/-X should use
|nextafterf()|/|nexttowardf()|
- variables declared via typeset -E/-X should use
|nextafter()|/|nexttoward()|
- variables declared via typeset -l -E/-X should use
|nextafterl()|/|nexttowardl()|
... if the platforms libc/libm do not have a matching
|nextafter(f|l|)\(\)|/|nexttoward(f|l|)\(\)| variant for the input
datatype then the "function not found"-error should be thrown.
Note that we do _not_ have to change the logic for all math
functions... AFAIK |nextafter()| and |nexttoward()| are the only
exceptions which require special handling...
Glenn: What do you think ?
Attached (as "astksh20130807_short_float_nextafter001.diff.txt") is a
_prototype_ patch which shows how it would look like:
-- snip --
$ ksh -c 'typeset -s -E x=4 ; print $(( x=nextafter(x,5) ))'
4.0000004768371582
$ ksh -c 'typeset -E x=4 ; print $(( x=nextafter(x,5) ))'
4.00000000000000089
$ ksh -c 'typeset -l -E x=4 ; print $(( x=nextafter(x,5) ))'
4 # this is not exactly 4 but it is so a tiny step away from 4 that
normal %f output doesn't recognise it
-- snip --

* ToDo:
- Add |nexttoward()| support
- Add defines for type size (|float|, |double|, |long double|)
- Add error code in case if one of the { |float|, |double|, |long
double| }-variants is missing
- Somehow make the code look better

Comments/rants/feedback welcome...

----

Bye,
Roland
--
__ . . __
(o.\ \/ /.o) roland.mainz at nrubsig.org
\__\/\/__/ MPEG specialist, C&&JAVA&&Sun&&Unix programmer
/O /==\ O\ TEL +49 641 3992797
(;O/ \/ \O;)
-------------- next part --------------
diff -r -u original/src/cmd/ksh93/sh/arith.c build_i386_64bit_debug/src/cmd/ksh93/sh/arith.c
--- src/cmd/ksh93/sh/arith.c 2013-06-04 22:14:14.000000000 +0200
+++ src/cmd/ksh93/sh/arith.c 2013-08-11 23:49:21.027203489 +0200
@@ -502,8 +502,12 @@
r = nv_getnum(np);
if(nv_isattr(np,NV_INTEGER|NV_BINARY)==(NV_INTEGER|NV_BINARY))
lvalue->isfloat= (r!=(Sflong_t)r);
+ else if(nv_isattr(np,(NV_DOUBLE|NV_SHORT))==(NV_DOUBLE|NV_SHORT))
+ lvalue->isfloat=2;
+ else if(nv_isattr(np,(NV_DOUBLE|NV_LONG))==(NV_DOUBLE|NV_LONG))
+ lvalue->isfloat=3;
else if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE)
- lvalue->isfloat=1;
+ lvalue->isfloat=4;
if((lvalue->emode&ARITH_ASSIGNOP) && nv_isarray(np))
lvalue->nosub = nv_aindex(np)+1;
return(r);
diff -r -u original/src/cmd/ksh93/sh/streval.c build_i386_64bit_debug/src/cmd/ksh93/sh/streval.c
--- src/cmd/ksh93/sh/streval.c 2013-07-23 20:48:04.000000000 +0200
+++ src/cmd/ksh93/sh/streval.c 2013-08-12 00:01:19.333351488 +0200
@@ -492,7 +492,28 @@
if(c&T_NOFLOAT)
num = (*((Math_2f_i)fun))(sp[1],(int)num);
else
- num = (*((Math_2f_f)fun))(sp[1],num);
+ {
+ if (((void*)fun) == ((void*)nextafterl))
+ {
+ switch(node.isfloat)
+ {
+ case 2:
+ num = (nextafterf)((float)sp[1],(float)num);
+ break;
+ case 3:
+ num = (nextafterl)((long double)sp[1],(long double)num);
+ break;
+ default:
+ case 4:
+ num = (nextafter)((double)sp[1],(double)num);
+ break;
+ }
+ }
+ else
+ {
+ num = (*((Math_2f_f)fun))(sp[1],num);
+ }
+ }
break;
case A_CALL2I:
sp-=2,tp-=2;
Roland Mainz
2013-08-11 22:28:43 UTC
Permalink
Post by Roland Mainz
On Sun, Aug 11, 2013 at 6:15 PM, Cedric Blancher
[snip]
But why does nextafter() misbehave if I want to use a datatype smaller
than "long double"? Accuracy is a good thing, but in this case we
iterate too fine-grained, meaning the code should iterate over the
smallest possible steps of a double, but not over the smallest
possible steps of a long double.
Does anyone have a good idea how to fix this in ksh?
Grumpf... yes. Technically I feared that day may come when
|nextafter()| and |nexttoward()| were added in ksh93... ;-/
The issue is more or less like this: Both |nextafter(f|l|)\(\)| and
|nexttoward(f|l|)\(\)| step over the smallest possible quantity for
the specific { |float|, |double|, |long double| }-datatype and
therefore (for example) using |nextafterl()| (intended for |long
double|) for a |float| doesn't work because it does so small steps
that they cannot be represented in a |float| ... that causes the
endless loop in Tina's example.
The fix would be to "remember" the datatype (e.g. { |float|,
|double|, |long double| }) for a given variable and pass that down to
|arith_exec()| and call the specific version of |nextafter()| and
- variables declared via typeset -s -E/-X should use
|nextafterf()|/|nexttowardf()|
- variables declared via typeset -E/-X should use
|nextafter()|/|nexttoward()|
- variables declared via typeset -l -E/-X should use
|nextafterl()|/|nexttowardl()|
... if the platforms libc/libm do not have a matching
|nextafter(f|l|)\(\)|/|nexttoward(f|l|)\(\)| variant for the input
datatype then the "function not found"-error should be thrown.
Note that we do _not_ have to change the logic for all math
functions... AFAIK |nextafter()| and |nexttoward()| are the only
exceptions which require special handling...
Glenn: What do you think ?
Attached (as "astksh20130807_short_float_nextafter001.diff.txt") is a
-- snip --
$ ksh -c 'typeset -s -E x=4 ; print $(( x=nextafter(x,5) ))'
4.0000004768371582
$ ksh -c 'typeset -E x=4 ; print $(( x=nextafter(x,5) ))'
4.00000000000000089
$ ksh -c 'typeset -l -E x=4 ; print $(( x=nextafter(x,5) ))'
4 # this is not exactly 4 but it is so a tiny step away from 4 that
normal %f output doesn't recognise it
-- snip --
- Add |nexttoward()| support
- Add defines for type size (|float|, |double|, |long double|)
- Add error code in case if one of the { |float|, |double|, |long
double| }-variants is missing
- Somehow make the code look better
Comments/rants/feedback welcome...
Grumpf... attached (as
"astksh20130807_short_float_nextafter002.diff.txt") is a fixed
patch... the previous one used |double| in case that the datatype of
the arguments couldn't be obtained... the patch corrects this and adds
support for |nexttoward()| ...

----

Bye,
Roland
--
__ . . __
(o.\ \/ /.o) roland.mainz at nrubsig.org
\__\/\/__/ MPEG specialist, C&&JAVA&&Sun&&Unix programmer
/O /==\ O\ TEL +49 641 3992797
(;O/ \/ \O;)
-------------- next part --------------
diff -r -u original/src/cmd/ksh93/sh/arith.c build_i386_64bit_debug/src/cmd/ksh93/sh/arith.c
--- src/cmd/ksh93/sh/arith.c 2013-06-04 22:14:14.000000000 +0200
+++ src/cmd/ksh93/sh/arith.c 2013-08-11 23:49:21.027203489 +0200
@@ -502,8 +502,12 @@
r = nv_getnum(np);
if(nv_isattr(np,NV_INTEGER|NV_BINARY)==(NV_INTEGER|NV_BINARY))
lvalue->isfloat= (r!=(Sflong_t)r);
+ else if(nv_isattr(np,(NV_DOUBLE|NV_SHORT))==(NV_DOUBLE|NV_SHORT))
+ lvalue->isfloat=2;
+ else if(nv_isattr(np,(NV_DOUBLE|NV_LONG))==(NV_DOUBLE|NV_LONG))
+ lvalue->isfloat=3;
else if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE)
- lvalue->isfloat=1;
+ lvalue->isfloat=4;
if((lvalue->emode&ARITH_ASSIGNOP) && nv_isarray(np))
lvalue->nosub = nv_aindex(np)+1;
return(r);
diff -r -u original/src/cmd/ksh93/sh/streval.c build_i386_64bit_debug/src/cmd/ksh93/sh/streval.c
--- src/cmd/ksh93/sh/streval.c 2013-07-23 20:48:04.000000000 +0200
+++ src/cmd/ksh93/sh/streval.c 2013-08-12 00:18:58.553584235 +0200
@@ -492,7 +492,44 @@
if(c&T_NOFLOAT)
num = (*((Math_2f_i)fun))(sp[1],(int)num);
else
- num = (*((Math_2f_f)fun))(sp[1],num);
+ {
+ if (((void*)fun) == ((void*)nextafterl))
+ {
+ switch(node.isfloat)
+ {
+ case 2:
+ num = (nextafterf)((float)sp[1],(float)num);
+ break;
+ case 4:
+ num = (nextafter)((double)sp[1],(double)num);
+ break;
+ default:
+ case 3:
+ num = (nextafterl)((long double)sp[1],(long double)num);
+ break;
+ }
+ }
+ else if (((void*)fun) == ((void*)nexttowardl))
+ {
+ switch(node.isfloat)
+ {
+ case 2:
+ num = (nexttowardf)((float)sp[1],(float)num);
+ break;
+ case 4:
+ num = (nexttoward)((double)sp[1],(double)num);
+ break;
+ default:
+ case 3:
+ num = (nexttowardl)((long double)sp[1],(long double)num);
+ break;
+ }
+ }
+ else
+ {
+ num = (*((Math_2f_f)fun))(sp[1],num);
+ }
+ }
break;
case A_CALL2I:
sp-=2,tp-=2;
Roland Mainz
2013-08-11 23:16:46 UTC
Permalink
On Mon, Aug 12, 2013 at 12:28 AM, Roland Mainz <roland.mainz at nrubsig.org> wrote:
[Removing ast-users at lists.research.att.com]
Post by Roland Mainz
Post by Roland Mainz
On Sun, Aug 11, 2013 at 6:15 PM, Cedric Blancher
[snip]
But why does nextafter() misbehave if I want to use a datatype smaller
than "long double"? Accuracy is a good thing, but in this case we
iterate too fine-grained, meaning the code should iterate over the
smallest possible steps of a double, but not over the smallest
possible steps of a long double.
Does anyone have a good idea how to fix this in ksh?
Grumpf... yes. Technically I feared that day may come when
|nextafter()| and |nexttoward()| were added in ksh93... ;-/
The issue is more or less like this: Both |nextafter(f|l|)\(\)| and
|nexttoward(f|l|)\(\)| step over the smallest possible quantity for
the specific { |float|, |double|, |long double| }-datatype and
therefore (for example) using |nextafterl()| (intended for |long
double|) for a |float| doesn't work because it does so small steps
that they cannot be represented in a |float| ... that causes the
endless loop in Tina's example.
The fix would be to "remember" the datatype (e.g. { |float|,
|double|, |long double| }) for a given variable and pass that down to
|arith_exec()| and call the specific version of |nextafter()| and
- variables declared via typeset -s -E/-X should use
|nextafterf()|/|nexttowardf()|
- variables declared via typeset -E/-X should use
|nextafter()|/|nexttoward()|
- variables declared via typeset -l -E/-X should use
|nextafterl()|/|nexttowardl()|
... if the platforms libc/libm do not have a matching
|nextafter(f|l|)\(\)|/|nexttoward(f|l|)\(\)| variant for the input
datatype then the "function not found"-error should be thrown.
Note that we do _not_ have to change the logic for all math
functions... AFAIK |nextafter()| and |nexttoward()| are the only
exceptions which require special handling...
Glenn: What do you think ?
Attached (as "astksh20130807_short_float_nextafter001.diff.txt") is a
-- snip --
$ ksh -c 'typeset -s -E x=4 ; print $(( x=nextafter(x,5) ))'
4.0000004768371582
$ ksh -c 'typeset -E x=4 ; print $(( x=nextafter(x,5) ))'
4.00000000000000089
$ ksh -c 'typeset -l -E x=4 ; print $(( x=nextafter(x,5) ))'
4 # this is not exactly 4 but it is so a tiny step away from 4 that
normal %f output doesn't recognise it
-- snip --
- Add |nexttoward()| support
- Add defines for type size (|float|, |double|, |long double|)
- Add error code in case if one of the { |float|, |double|, |long
double| }-variants is missing
- Somehow make the code look better
Comments/rants/feedback welcome...
Grumpf... attached (as
"astksh20130807_short_float_nextafter002.diff.txt") is a fixed
patch... the previous one used |double| in case that the datatype of
the arguments couldn't be obtained... the patch corrects this and adds
support for |nexttoward()| ...
More thought about this:
src/cmd/ksh93/data/math.tab could return all three variants (for {
|float|, |double|, |long double| }) and |fun| in |arith_exec()| would
be a pointer to an array of these three variants. That would make the
support for |float| and |double| generic and remove all the
|if()|/|switch()| mess from the "hot" codepath...

... Glenn/David: What do you think ?

----

Bye,
Roland
--
__ . . __
(o.\ \/ /.o) roland.mainz at nrubsig.org
\__\/\/__/ MPEG specialist, C&&JAVA&&Sun&&Unix programmer
/O /==\ O\ TEL +49 641 3992797
(;O/ \/ \O;)
Irek Szczesniak
2013-08-12 05:29:08 UTC
Permalink
Post by Roland Mainz
[Removing ast-users at lists.research.att.com]
Post by Roland Mainz
Post by Roland Mainz
On Sun, Aug 11, 2013 at 6:15 PM, Cedric Blancher
[snip]
But why does nextafter() misbehave if I want to use a datatype smaller
than "long double"? Accuracy is a good thing, but in this case we
iterate too fine-grained, meaning the code should iterate over the
smallest possible steps of a double, but not over the smallest
possible steps of a long double.
Does anyone have a good idea how to fix this in ksh?
Grumpf... yes. Technically I feared that day may come when
|nextafter()| and |nexttoward()| were added in ksh93... ;-/
The issue is more or less like this: Both |nextafter(f|l|)\(\)| and
|nexttoward(f|l|)\(\)| step over the smallest possible quantity for
the specific { |float|, |double|, |long double| }-datatype and
therefore (for example) using |nextafterl()| (intended for |long
double|) for a |float| doesn't work because it does so small steps
that they cannot be represented in a |float| ... that causes the
endless loop in Tina's example.
The fix would be to "remember" the datatype (e.g. { |float|,
|double|, |long double| }) for a given variable and pass that down to
|arith_exec()| and call the specific version of |nextafter()| and
- variables declared via typeset -s -E/-X should use
|nextafterf()|/|nexttowardf()|
- variables declared via typeset -E/-X should use
|nextafter()|/|nexttoward()|
- variables declared via typeset -l -E/-X should use
|nextafterl()|/|nexttowardl()|
... if the platforms libc/libm do not have a matching
|nextafter(f|l|)\(\)|/|nexttoward(f|l|)\(\)| variant for the input
datatype then the "function not found"-error should be thrown.
Note that we do _not_ have to change the logic for all math
functions... AFAIK |nextafter()| and |nexttoward()| are the only
exceptions which require special handling...
Glenn: What do you think ?
Attached (as "astksh20130807_short_float_nextafter001.diff.txt") is a
-- snip --
$ ksh -c 'typeset -s -E x=4 ; print $(( x=nextafter(x,5) ))'
4.0000004768371582
$ ksh -c 'typeset -E x=4 ; print $(( x=nextafter(x,5) ))'
4.00000000000000089
$ ksh -c 'typeset -l -E x=4 ; print $(( x=nextafter(x,5) ))'
4 # this is not exactly 4 but it is so a tiny step away from 4 that
normal %f output doesn't recognise it
-- snip --
- Add |nexttoward()| support
- Add defines for type size (|float|, |double|, |long double|)
- Add error code in case if one of the { |float|, |double|, |long
double| }-variants is missing
- Somehow make the code look better
Comments/rants/feedback welcome...
Grumpf... attached (as
"astksh20130807_short_float_nextafter002.diff.txt") is a fixed
patch... the previous one used |double| in case that the datatype of
the arguments couldn't be obtained... the patch corrects this and adds
support for |nexttoward()| ...
src/cmd/ksh93/data/math.tab could return all three variants (for {
|float|, |double|, |long double| }) and |fun| in |arith_exec()| would
be a pointer to an array of these three variants. That would make the
support for |float| and |double| generic and remove all the
|if()|/|switch()| mess from the "hot" codepath...
That is IMO the only solution which covers *all* corner cases, i.e. if
an overflow/underflow or creation of subnormal numbers in a math
function happens. Smaller datatypes mean you'll hit the limits earlier
than for larger datatypes and not all float/double functions behave
like doing the same operation with a long double datatype and then
cast the result to the requested datatype.

Irek
Tina Harriott
2013-08-12 09:15:00 UTC
Permalink
Post by Irek Szczesniak
Post by Roland Mainz
[Removing ast-users at lists.research.att.com]
Post by Roland Mainz
Post by Roland Mainz
On Sun, Aug 11, 2013 at 6:15 PM, Cedric Blancher
[snip]
But why does nextafter() misbehave if I want to use a datatype smaller
than "long double"? Accuracy is a good thing, but in this case we
iterate too fine-grained, meaning the code should iterate over the
smallest possible steps of a double, but not over the smallest
possible steps of a long double.
Does anyone have a good idea how to fix this in ksh?
Grumpf... yes. Technically I feared that day may come when
|nextafter()| and |nexttoward()| were added in ksh93... ;-/
The issue is more or less like this: Both |nextafter(f|l|)\(\)| and
|nexttoward(f|l|)\(\)| step over the smallest possible quantity for
the specific { |float|, |double|, |long double| }-datatype and
therefore (for example) using |nextafterl()| (intended for |long
double|) for a |float| doesn't work because it does so small steps
that they cannot be represented in a |float| ... that causes the
endless loop in Tina's example.
The fix would be to "remember" the datatype (e.g. { |float|,
|double|, |long double| }) for a given variable and pass that down to
|arith_exec()| and call the specific version of |nextafter()| and
- variables declared via typeset -s -E/-X should use
|nextafterf()|/|nexttowardf()|
- variables declared via typeset -E/-X should use
|nextafter()|/|nexttoward()|
- variables declared via typeset -l -E/-X should use
|nextafterl()|/|nexttowardl()|
... if the platforms libc/libm do not have a matching
|nextafter(f|l|)\(\)|/|nexttoward(f|l|)\(\)| variant for the input
datatype then the "function not found"-error should be thrown.
Note that we do _not_ have to change the logic for all math
functions... AFAIK |nextafter()| and |nexttoward()| are the only
exceptions which require special handling...
Glenn: What do you think ?
Attached (as "astksh20130807_short_float_nextafter001.diff.txt") is a
-- snip --
$ ksh -c 'typeset -s -E x=4 ; print $(( x=nextafter(x,5) ))'
4.0000004768371582
$ ksh -c 'typeset -E x=4 ; print $(( x=nextafter(x,5) ))'
4.00000000000000089
$ ksh -c 'typeset -l -E x=4 ; print $(( x=nextafter(x,5) ))'
4 # this is not exactly 4 but it is so a tiny step away from 4 that
normal %f output doesn't recognise it
-- snip --
- Add |nexttoward()| support
- Add defines for type size (|float|, |double|, |long double|)
- Add error code in case if one of the { |float|, |double|, |long
double| }-variants is missing
- Somehow make the code look better
Comments/rants/feedback welcome...
Grumpf... attached (as
"astksh20130807_short_float_nextafter002.diff.txt") is a fixed
patch... the previous one used |double| in case that the datatype of
the arguments couldn't be obtained... the patch corrects this and adds
support for |nexttoward()| ...
src/cmd/ksh93/data/math.tab could return all three variants (for {
|float|, |double|, |long double| }) and |fun| in |arith_exec()| would
be a pointer to an array of these three variants. That would make the
support for |float| and |double| generic and remove all the
|if()|/|switch()| mess from the "hot" codepath...
That is IMO the only solution which covers *all* corner cases, i.e. if
an overflow/underflow or creation of subnormal numbers in a math
function happens. Smaller datatypes mean you'll hit the limits earlier
than for larger datatypes and not all float/double functions behave
like doing the same operation with a long double datatype and then
cast the result to the requested datatype.
Irek, I looked at this. Using a wider datatype and then cast to the
narrower datatype yields an error only in the last bit:
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

int main()
{
long double al;
double a;
double al_d;
long double r;

a = tan(-0.001);
al = tanl(-0.001);

al_d = al;

r = fabs(a-al_d);

printf("diff=%a\n", r);

return 0;
}
Post by Irek Szczesniak
gcc -g x.c
./a.out
diff=0x0.0000000000001p-1022

This test however doesn't cover exceptional conditions: float will
overflow much faster than a double, a condition which affects pow(),
and hypot() will behave differently for subnormal inputs of different
types because the classification of subnormal depends on the width of
the datatype. Storing results from a narrow datatype (i.e. float,
double) in a long double however is OK again because no information is
lost in that direction.

Therefore I agree with you that all math functions should only be used
with the appropriate datatypes.

Tina
--
Tina Harriott - Women in Mathematics
Contact: tina.harriott.math at gmail.com
Glenn Fowler
2013-08-12 17:10:07 UTC
Permalink
Post by Tina Harriott
Post by Irek Szczesniak
Post by Roland Mainz
[Removing ast-users at lists.research.att.com]
Post by Roland Mainz
Post by Roland Mainz
On Sun, Aug 11, 2013 at 6:15 PM, Cedric Blancher
[snip]
But why does nextafter() misbehave if I want to use a datatype smaller
than "long double"? Accuracy is a good thing, but in this case we
iterate too fine-grained, meaning the code should iterate over the
smallest possible steps of a double, but not over the smallest
possible steps of a long double.
Does anyone have a good idea how to fix this in ksh?
Grumpf... yes. Technically I feared that day may come when
|nextafter()| and |nexttoward()| were added in ksh93... ;-/
The issue is more or less like this: Both |nextafter(f|l|)\(\)| and
|nexttoward(f|l|)\(\)| step over the smallest possible quantity for
the specific { |float|, |double|, |long double| }-datatype and
therefore (for example) using |nextafterl()| (intended for |long
double|) for a |float| doesn't work because it does so small steps
that they cannot be represented in a |float| ... that causes the
endless loop in Tina's example.
The fix would be to "remember" the datatype (e.g. { |float|,
|double|, |long double| }) for a given variable and pass that down to
|arith_exec()| and call the specific version of |nextafter()| and
- variables declared via typeset -s -E/-X should use
|nextafterf()|/|nexttowardf()|
- variables declared via typeset -E/-X should use
|nextafter()|/|nexttoward()|
- variables declared via typeset -l -E/-X should use
|nextafterl()|/|nexttowardl()|
... if the platforms libc/libm do not have a matching
|nextafter(f|l|)\(\)|/|nexttoward(f|l|)\(\)| variant for the input
datatype then the "function not found"-error should be thrown.
Note that we do _not_ have to change the logic for all math
functions... AFAIK |nextafter()| and |nexttoward()| are the only
exceptions which require special handling...
Glenn: What do you think ?
Attached (as "astksh20130807_short_float_nextafter001.diff.txt") is a
-- snip --
$ ksh -c 'typeset -s -E x=4 ; print $(( x=nextafter(x,5) ))'
4.0000004768371582
$ ksh -c 'typeset -E x=4 ; print $(( x=nextafter(x,5) ))'
4.00000000000000089
$ ksh -c 'typeset -l -E x=4 ; print $(( x=nextafter(x,5) ))'
4 # this is not exactly 4 but it is so a tiny step away from 4 that
normal %f output doesn't recognise it
-- snip --
- Add |nexttoward()| support
- Add defines for type size (|float|, |double|, |long double|)
- Add error code in case if one of the { |float|, |double|, |long
double| }-variants is missing
- Somehow make the code look better
Comments/rants/feedback welcome...
Grumpf... attached (as
"astksh20130807_short_float_nextafter002.diff.txt") is a fixed
patch... the previous one used |double| in case that the datatype of
the arguments couldn't be obtained... the patch corrects this and adds
support for |nexttoward()| ...
src/cmd/ksh93/data/math.tab could return all three variants (for {
|float|, |double|, |long double| }) and |fun| in |arith_exec()| would
be a pointer to an array of these three variants. That would make the
support for |float| and |double| generic and remove all the
|if()|/|switch()| mess from the "hot" codepath...
That is IMO the only solution which covers *all* corner cases, i.e. if
an overflow/underflow or creation of subnormal numbers in a math
function happens. Smaller datatypes mean you'll hit the limits earlier
than for larger datatypes and not all float/double functions behave
like doing the same operation with a long double datatype and then
cast the result to the requested datatype.
Irek, I looked at this. Using a wider datatype and then cast to the
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
int main()
{
long double al;
double a;
double al_d;
long double r;
a = tan(-0.001);
al = tanl(-0.001);
al_d = al;
r = fabs(a-al_d);
printf("diff=%a\n", r);
this should be

printf("diff=%La\n", r);

Glenn Fowler
2013-08-12 17:07:47 UTC
Permalink
Post by Irek Szczesniak
Post by Roland Mainz
[Removing ast-users at lists.research.att.com]
Post by Roland Mainz
Post by Roland Mainz
On Sun, Aug 11, 2013 at 6:15 PM, Cedric Blancher
[snip]
But why does nextafter() misbehave if I want to use a datatype smaller
than "long double"? Accuracy is a good thing, but in this case we
iterate too fine-grained, meaning the code should iterate over the
smallest possible steps of a double, but not over the smallest
possible steps of a long double.
Does anyone have a good idea how to fix this in ksh?
Grumpf... yes. Technically I feared that day may come when
|nextafter()| and |nexttoward()| were added in ksh93... ;-/
The issue is more or less like this: Both |nextafter(f|l|)\(\)| and
|nexttoward(f|l|)\(\)| step over the smallest possible quantity for
the specific { |float|, |double|, |long double| }-datatype and
therefore (for example) using |nextafterl()| (intended for |long
double|) for a |float| doesn't work because it does so small steps
that they cannot be represented in a |float| ... that causes the
endless loop in Tina's example.
The fix would be to "remember" the datatype (e.g. { |float|,
|double|, |long double| }) for a given variable and pass that down to
|arith_exec()| and call the specific version of |nextafter()| and
- variables declared via typeset -s -E/-X should use
|nextafterf()|/|nexttowardf()|
- variables declared via typeset -E/-X should use
|nextafter()|/|nexttoward()|
- variables declared via typeset -l -E/-X should use
|nextafterl()|/|nexttowardl()|
... if the platforms libc/libm do not have a matching
|nextafter(f|l|)\(\)|/|nexttoward(f|l|)\(\)| variant for the input
datatype then the "function not found"-error should be thrown.
Note that we do _not_ have to change the logic for all math
functions... AFAIK |nextafter()| and |nexttoward()| are the only
exceptions which require special handling...
Glenn: What do you think ?
Attached (as "astksh20130807_short_float_nextafter001.diff.txt") is a
-- snip --
$ ksh -c 'typeset -s -E x=4 ; print $(( x=nextafter(x,5) ))'
4.0000004768371582
$ ksh -c 'typeset -E x=4 ; print $(( x=nextafter(x,5) ))'
4.00000000000000089
$ ksh -c 'typeset -l -E x=4 ; print $(( x=nextafter(x,5) ))'
4 # this is not exactly 4 but it is so a tiny step away from 4 that
normal %f output doesn't recognise it
-- snip --
- Add |nexttoward()| support
- Add defines for type size (|float|, |double|, |long double|)
- Add error code in case if one of the { |float|, |double|, |long
double| }-variants is missing
- Somehow make the code look better
Comments/rants/feedback welcome...
Grumpf... attached (as
"astksh20130807_short_float_nextafter002.diff.txt") is a fixed
patch... the previous one used |double| in case that the datatype of
the arguments couldn't be obtained... the patch corrects this and adds
support for |nexttoward()| ...
src/cmd/ksh93/data/math.tab could return all three variants (for {
|float|, |double|, |long double| }) and |fun| in |arith_exec()| would
be a pointer to an array of these three variants. That would make the
support for |float| and |double| generic and remove all the
|if()|/|switch()| mess from the "hot" codepath...
That is IMO the only solution which covers *all* corner cases, i.e. if
an overflow/underflow or creation of subnormal numbers in a math
function happens. Smaller datatypes mean you'll hit the limits earlier
than for larger datatypes and not all float/double functions behave
like doing the same operation with a long double datatype and then
cast the result to the requested datatype.
a related question
does posix fprintf(3) have a way to print (float) values?
I looked a few times and didn't find any
Glenn Fowler
2013-08-12 16:39:37 UTC
Permalink
Post by Roland Mainz
[Removing ast-users at lists.research.att.com]
Post by Roland Mainz
Post by Roland Mainz
On Sun, Aug 11, 2013 at 6:15 PM, Cedric Blancher
[snip]
But why does nextafter() misbehave if I want to use a datatype smaller
than "long double"? Accuracy is a good thing, but in this case we
iterate too fine-grained, meaning the code should iterate over the
smallest possible steps of a double, but not over the smallest
possible steps of a long double.
Does anyone have a good idea how to fix this in ksh?
Grumpf... yes. Technically I feared that day may come when
|nextafter()| and |nexttoward()| were added in ksh93... ;-/
The issue is more or less like this: Both |nextafter(f|l|)\(\)| and
|nexttoward(f|l|)\(\)| step over the smallest possible quantity for
the specific { |float|, |double|, |long double| }-datatype and
therefore (for example) using |nextafterl()| (intended for |long
double|) for a |float| doesn't work because it does so small steps
that they cannot be represented in a |float| ... that causes the
endless loop in Tina's example.
The fix would be to "remember" the datatype (e.g. { |float|,
|double|, |long double| }) for a given variable and pass that down to
|arith_exec()| and call the specific version of |nextafter()| and
- variables declared via typeset -s -E/-X should use
|nextafterf()|/|nexttowardf()|
- variables declared via typeset -E/-X should use
|nextafter()|/|nexttoward()|
- variables declared via typeset -l -E/-X should use
|nextafterl()|/|nexttowardl()|
... if the platforms libc/libm do not have a matching
|nextafter(f|l|)\(\)|/|nexttoward(f|l|)\(\)| variant for the input
datatype then the "function not found"-error should be thrown.
Note that we do _not_ have to change the logic for all math
functions... AFAIK |nextafter()| and |nexttoward()| are the only
exceptions which require special handling...
Glenn: What do you think ?
Attached (as "astksh20130807_short_float_nextafter001.diff.txt") is a
-- snip --
$ ksh -c 'typeset -s -E x=4 ; print $(( x=nextafter(x,5) ))'
4.0000004768371582
$ ksh -c 'typeset -E x=4 ; print $(( x=nextafter(x,5) ))'
4.00000000000000089
$ ksh -c 'typeset -l -E x=4 ; print $(( x=nextafter(x,5) ))'
4 # this is not exactly 4 but it is so a tiny step away from 4 that
normal %f output doesn't recognise it
-- snip --
- Add |nexttoward()| support
- Add defines for type size (|float|, |double|, |long double|)
- Add error code in case if one of the { |float|, |double|, |long
double| }-variants is missing
- Somehow make the code look better
Comments/rants/feedback welcome...
Grumpf... attached (as
"astksh20130807_short_float_nextafter002.diff.txt") is a fixed
patch... the previous one used |double| in case that the datatype of
the arguments couldn't be obtained... the patch corrects this and adds
support for |nexttoward()| ...
src/cmd/ksh93/data/math.tab could return all three variants (for {
|float|, |double|, |long double| }) and |fun| in |arith_exec()| would
be a pointer to an array of these three variants. That would make the
support for |float| and |double| generic and remove all the
|if()|/|switch()| mess from the "hot" codepath...
... Glenn/David: What do you think ?
any fix should include math.tab and features/math.sh
I'll me with dgk shortly
Loading...