October 30, 2025, 05:32:16 PM

News:

IWBasic runs in Windows 11!


NaN support

Started by DrT, October 08, 2012, 05:28:01 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

DrT

If x = 0.0 and y = 0.0, then I would like z = x / y = NaN.  Also, I would like any mathematical operation that involves NaN to return NaN.

Right now, z returns 0.0.

Is there a way to enable NaN support?

LarryMc

I know of no way other than to put your equations in subroutines; pass in your variables and then test for each of the possible conditions that could result in a NaN.

Maybe someone else has some ideas.
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

GWS

It's always been the programmer's responsibility to handle troublesome data.
In the real world, data can always be expected to contain occasional weird values.

A program should protect against division by zero, square roots and logarithms of negative numbers, the inverse sine or cosine of a number that is less than âˆ'1 or greater than +1.

What you do about it is down to the application.  If its a small personal calculation, just show an error message and stop, is probably the best thing to do.

If it's a large commercial program, you can't just stop processing - so maybe just dump the defective transaction to an error file, and proceed is maybe the way to go.  Hopefully, it won't happen very often.

There is nothing in Basic to automatically handle these problems, which can crop up in many ways.

If you are inverting a matrix for instance, your algorithm might have worked many times successfully, but if for instance, you have very large and very small numbers among the elements, you may suffer loss of precision and come up with a useless answer.  You can check for this by multiplying the inverse with the original matrix.  The answer should be 1.  If not, it's gone horribly wrong, and an error message is called for.

One worrying thing, Creative Basic gives an error message if you try to divide by zero.  IWB doesn't, and you get a strange value which I don't understand.  Try this:


openconsole
cls

x = 0.0
y = 0.0

print x/y

do:until inkey$<>""
closeconsole
end


I get -1.#J  ... whatever that is ..  ::)  No error message.

Happy days .. in programming there's always been something to scratch your head about ..

all the best, :)

Graham
Tomorrow may be too late ..

LarryMc

Graham

It appears that -1.#J is what you get when you do something you shouldn't.
I got -1.#J for SQRT(-2) and for 0.0/0.0
but if a=0 and b=0  then a/b gives a divide by zero crash.

I looked through the documentation and can't find any reference.
I also looked through my correspondence with Sapero and find no mention of it.
I don't know if it is something that has always been there or something  Sapero added and hadn't mentioned it yet when he disappeared.
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

GWS

Hmm .. looks like an attempt to implement a NAN:

Quote
Different operating systems and programming languages may have different string representations of NaN.
nan
NaN
NaN%
NAN
NaNQ
NaNS
qNaN
sNaN
1.#SNAN
1.#QNAN
-1.#IND


http://en.wikipedia.org/wiki/NaN

For me, it seems to implement more trouble than it's worth .. the programmer is still left to deal with the problem, but now the language is made more complicated.

Perhaps best left to IEEE standards in Fortran ..  :)

all the best,

Graham
Tomorrow may be too late ..

Brian

And there was I, thinking NaN was some kind of bread!

Brian

GWS

October 09, 2012, 07:11:21 AM #6 Last Edit: October 09, 2012, 08:14:01 AM by GWS
Brian .. Nah .. thats NaaN (Not another awful number) .. ;D

I've been checking back and it was in an older version of EBasic (1.737).

I couldn't find an old version of IBPro to check that.

I did find this discussion on the old Pyxia Forum file:

Quote
Joske 2004

I need a routine that can detect if an overflow occured in a double, to prevent a math program from crashing.

For IBasic Std I had a working solution: convert the double to a string, and then check if "#INF" is in this string, in that case there is an overflow.

But this doesn't work for IBasic Pro. I have a solution that partly works:
Code:
'test for detecting overflow (try different values)
double value
string test
'value=2.5e200   :'this is no overflow
value=2.5^800   :'this will return an overflow message
'value=2.5e400   :'this will let the program crash
print value
test = using("#.##", value)   :print "test="+test
if test="1.#J" then print "overflow!"
print ""
print "press any key..."
do:until inkey$<>""


Does somebody know why the program crashes when you for example try value=2.5e400?

How to solve this problem? It seems like IBpro isn't able to convert an double with overflow to a string.

Jos

__________________

Just change the precision. No crash here at all with any values.

Code:
'test for detecting overflow (try different values)
double value
string test
SETPRECISION 4
'value=2.5e200   :'this is no overflow
'value=2.5^800   :'this will return an overflow message
value=2.5e400   :'this will let the program crash
print value
test = using("#.####", value)   :print "test="+test
if test="1.#INF" then print "overflow!"
print ""
print "press any key..."
do:until inkey$<>""




With only 2 digit precision the conversion routine returns #j with j meaning an imaginary number or non-existant.

You can of course just convert to a UINT64 and test the overflow number yourself.

Remembering the a DOUBLE precisions number is stored as 64 bits in memory. So for a quick test of special numbers test both halves of the 64 bits.

Code:
SUB IsOverflow(test as DOUBLE), INT
DEF pTest as POINTER
pTest = test
IF #<UINT>pTest[1] = 0x7FF00000 AND #<UINT>pTest[0] = 0 THEN RETURN TRUE
RETURN FALSE
ENDSUB


According to the Intel docs
0x7FF00000 00000000 = Overflow

And there are other methods, a little more too deep for me to get into like setting a software interrupt that gets called everytime the processor overflows.

Paul.
____________

Actually it was -Infinitiy. Here are all the test numbers:

Code:
64-bit
-Infinity 7FF00000 00000000
+Infinity FFF00000 00000000
Indefinite FFF80000 00000000


So you could modify the routine a bit to test for more than one condition.

Code:
SUB IsInfinite(test as DOUBLE), INT
DEF pTest as POINTER
pTest = test
IF #<UINT>pTest[1] = 0x7FF00000 AND #<UINT>pTest[0] = 0 THEN RETURN TRUE
IF #<UINT>pTest[1] = 0xFFF00000 AND #<UINT>pTest[0] = 0 THEN RETURN TRUE
RETURN FALSE
ENDSUB


For FLOAT types the test would be:
Code:
32-bit
-Infinity 7F800000
+Infinity FF800000
Indefinite FFC00000


Code:
SUB IsInfiniteF(test as FLOAT), INT
DEF pTest as POINTER
pTest = test
IF #<UINT>pTest = 0x7F800000 THEN RETURN TRUE
IF #<UINT>pTest = 0xFF800000 THEN RETURN TRUE
RETURN FALSE
ENDSUB


Hope that helps.

Paul.

_______________

And I should also note that you shoult keep the inifinity test and indefinite test as separate thing. Indefinite (NaN) means just that..its not defined. Such as log(-1) Which of course is not the same as an infinite overflow.

Code:
value = log(-1)
IF IsIndefinite(value) THEN PRINT "Undefined!"
...
SUB IsIndefinite(test as DOUBLE), INT
DEF pTest as POINTER
pTest = test
IF #<UINT>pTest[1] = 0xFFF80000 AND #<UINT>pTest[0] = 0 THEN RETURN TRUE
RETURN FALSE
ENDSUB


__________

Joske:

wow! thanks for this detailed explanation!

thank you very much :D
____________

still problems with some values, values around 1e300:
the subroutines returns false, but the program is not able to convert the double to a string, it crashes than.

Code:
'test for detecting overflow (try different values)
setprecision 12
double value
string test
value=2.5e300   :'this will let the program crash
print value
print ""
if isoverflow(value)=true then print "overflow!" else print "no overflow"
if isinfinite(value)=true then print "infinite!" else print "no infinite"
if isindefinite(value)=true then print "indefinite!" else print "no indefinite"
print ""
if (isoverflow(value)=false) and (isinfinite(value)=false) and (isindefinite(value)=false)
   test=using("#.###", value)
   print test
else
   print "can not print the value"
endif
print ""
print "press any key..."
do:until inkey$<>""
end
SUB IsOverflow(test as DOUBLE), INT
DEF pTest as POINTER
pTest = test
IF #<UINT>pTest[1] = 0x7FF00000 AND #<UINT>pTest[0] = 0 THEN RETURN TRUE
RETURN FALSE
ENDSUB
SUB IsInfinite(test as DOUBLE), INT
DEF pTest as POINTER
pTest = test
IF #<UINT>pTest[1] = 0x7FF00000 AND #<UINT>pTest[0] = 0 THEN RETURN TRUE
IF #<UINT>pTest[1] = 0xFFF00000 AND #<UINT>pTest[0] = 0 THEN RETURN TRUE
RETURN FALSE
ENDSUB
SUB IsIndefinite(test as DOUBLE), INT
DEF pTest as POINTER
pTest = test
IF #<UINT>pTest[1] = 0xFFF80000 AND #<UINT>pTest[0] = 0 THEN RETURN TRUE
RETURN FALSE
ENDSUB

___________

Joske,

You should test for both infinite cases as stated above.

0x7FF00000 00000000

Is only one side of infinity. I correct myself and gave you the second subroutine

And its crashing because your exceeding the limits of USING$ which can only handle a maximum of 255 digits at the moment. Guess I will have to expand it a bit.

2.5e300 is 25 with 299 zeros after it + a decimal point and the precision setting of 12 which is 312 character buffer needed.

So use the C runtime library to help yourself out.

Code:
DECLARE CDECL EXTERN _sprintf(buffer as string,format as STRING,...),INT
'test for detecting overflow (try different values)
setprecision 4
double value
'large buffer for large values
istring test[512]
value=2.5e300   
'print value
'print ""
if isoverflow(value)=true then print "overflow!" else print "no overflow"
if isinfinite(value)=true then print "infinite!" else print "no infinite"
if isindefinite(value)=true then print "indefinite!" else print "no indefinite"
print ""
if (isoverflow(value)=false) and (isinfinite(value)=false) and (isindefinite(value)=false)
   _sprintf(test,"%.12f",value)
   print test
else
   print "can not print the value"
endif
print ""
print "press any key..."
do:until inkey$<>""
end
SUB IsOverflow(test as DOUBLE), INT
DEF pTest as POINTER
pTest = test
IF #<UINT>pTest[1] = 0x7FF00000 AND #<UINT>pTest[0] = 0 THEN RETURN TRUE
RETURN FALSE
ENDSUB
SUB IsInfinite(test as DOUBLE), INT
DEF pTest as POINTER
pTest = test
IF #<UINT>pTest[1] = 0x7FF00000 AND #<UINT>pTest[0] = 0 THEN RETURN TRUE
IF #<UINT>pTest[1] = 0xFFF00000 AND #<UINT>pTest[0] = 0 THEN RETURN TRUE
RETURN FALSE
ENDSUB
SUB IsIndefinite(test as DOUBLE), INT
DEF pTest as POINTER
pTest = test
IF #<UINT>pTest[1] = 0xFFF80000 AND #<UINT>pTest[0] = 0 THEN RETURN TRUE
RETURN FALSE
ENDSUB



The format string of _sprintf can handle exponent display too.

_sprintf(test,"%.2e",value)

Which is '2' digits after the decimal point, display as x.xxe(+-)x So your number would be spit back as

2.50e+300

Or you can specify a large E too

_sprintf(test,"%.2E",value)

2.50E+300

Paul.

_____________

Joske:

Paul Turley Wrote:
And its crashing because your exceeding the limits of USING$ which can only handle a maximum of 255 digits at the moment. Guess I will have to expand it a bit.


aha!!! of course! that couses the problem...
underwhile I have found another way that also seems to work:
with str$() in place of using(), and with setprecision

Code:
'test for detecting overflow (try different values)
setprecision 16
double value
istring test[400]
'value=4*atan(1)
'value=2.5e200
value=2.5e300
'value=2.5e320
'value=2.5e500
test = str$(value)
if instr(test, "#INF")>0
   print "!!! OVERFLOW !!!"
else
   print "no overflow"
endif
print test
print ""
print "press any key..."
do:until inkey$<>""
end


thanks, Paul!
I will play with al these stuff a little bit more to realy understand all this...

_______________

STR$ has a smaller limit. Which I am also changing. So don't depend on it yet if your printing 300 digit number ;)

Paul.
_______________

ZeroDog:

If all programmers took the time to use these methods to avoid overflows, we wouldn't have so many worms and holes in this digital world of ours ;)

_______________



So maybe that gives some clues ..

all the best, :)

Graham
Tomorrow may be too late ..

DrT

October 09, 2012, 08:09:31 AM #7 Last Edit: October 09, 2012, 11:37:16 AM by DrT
It appears that NaN is at least partially supported.  I'll need to test it further to see how it propagates through mathematical operations.

Graham's first post revealed the problem.  He was printing x/y while I was printing Str$(x/y).

Graham got -1.#J for 0.0 / 0.0.  Following with his example, you get 1.#J for 1/0.0 and -1.#J for -1/0.0.  If you add SetPrecision(15) to his example, you get -1.#IND for 0.0 / 0.0, 1.#INF for 1/0.0 and -1.#INF for -1/0.0.  I guess the N rounded the I to a J.  ???

The problem for now is with Str$(z).  If z = -1.#IND, then Str$(z) returns 0.0.  This is wrong.  If z = 1.#INF or -1.#INF, then Str$(z) returns 1.#INF or -1.#INF as expected.
 

DrT

October 09, 2012, 04:57:28 PM #8 Last Edit: October 09, 2012, 05:02:20 PM by DrT
I created the following subroutines that may be of interest to others.  The last one (_Str$()) addresses the problem I noted earlier:  Str$(NaN) <> 0.  I'll report this in the IWB 2.0 Bug Reports.


SUB NaN(), double
RETURN 0.0 / 0.0
ENDSUB

SUB Infinity(), double
RETURN 1.0 / 0.0
ENDSUB

SUB IsNaN(double num), int
RETURN (num = -num) ? 1 : 0
ENDSUB

SUB IsInfinity(double num), int
RETURN (num = Infinity() OR num = -Infinity()) ? 1 : 0
ENDSUB

SUB _Str$(double num), string
IF IsNaN(num)
RETURN "NaN"
ELSEIF IsInfinity(num)
IF num < 0
RETURN "-Infinity"
ELSE
RETURN "Infinity"
ENDIF
ELSE
RETURN STR$(num)
ENDIF
ENDSUB


I also tested the following functions to see how they would handle NaN, Infinity and -Infinity:
COS, SIN, TAN, ACOS, ASIN, ATAN, EXP, LOG, CEIL, FLOOR, COSH, SINH, TANH and SQRT.  Except for EXP, COSH, SINH and TANH all behaved as expected.

The value that should be returned is commented on the left at the bottom.  The value that was returned is commented on the right at the bottom.


' ***** test EXP *****

/*
SETPRECISION(15)
OPENCONSOLE
PRINT EXP(NaN())
PRINT EXP(Infinity())
PRINT EXP(-Infinity())
DO : UNTIL INKEY$ <> ""
CLOSECONSOLE
END
*/

' NaN
' Infinity !!!!! Returns NaN !!!!!
' 0 !!!!! Returns NaN !!!!!



' ***** test COSH = (exp(x) + exp(-x))/2 *****

/*
SETPRECISION(15)
OPENCONSOLE
PRINT COSH(NaN())
PRINT COSH(Infinity())
PRINT COSH(-Infinity())
DO : UNTIL INKEY$ <> ""
CLOSECONSOLE
END
*/

' NaN !!!!! Returns 1.@QNAN !!!!!
' Infinity !!!!! Returns NaN !!!!!
' Infinity !!!!! Returns NaN !!!!!



' ***** test SINH = (exp(x) - exp(-x))/2 *****

/*
SETPRECISION(15)
OPENCONSOLE
PRINT SINH(NaN())
PRINT SINH(Infinity())
PRINT SINH(-Infinity())
DO : UNTIL INKEY$ <> ""
CLOSECONSOLE
END
*/

' NaN !!!!! Returns 1.@QNAN !!!!!
' Infinity !!!!! Returns NaN !!!!!
' -Infinity !!!!! Returns NaN !!!!!



' ***** test TANH = (exp(x) - exp(-x))/(exp(x) + exp(-x)) *****

/*
SETPRECISION(15)
OPENCONSOLE
PRINT TANH(NaN())
PRINT TANH(Infinity())
PRINT TANH(-Infinity())
DO : UNTIL INKEY$ <> ""
CLOSECONSOLE
END
*/

' NaN !!!!! Returns 1.@QNAN !!!!!
' NaN
' NaN


The problems with COSH, SINH and TANH probably stem from the problems with EXP since they are expressed in terms of this function.

I'll report these in the IW2.0 Bug Reports.