Hello Charles
In PB they have an Integer function
which Convert a numeric expression to an integer
y = INT(numeric_expression)
see http://www.manmrk.net/tutorials/basic/PowerBASIC/pbcc6/int_function.html (http://www.manmrk.net/tutorials/basic/PowerBASIC/pbcc6/int_function.html)
so in O2, can we have something like
Long Tmpnum , Tmpnum2
Tmpnum = 405
Tmpnum = INT(Tmpnum / 16)
Tmpnum2 = INT( Tmpnum\16 )
is this syntaxly correct ? as i'm using Long for Tmpnum and Tmpnum2
i have compiled the below code
' IntegerFun.o2bas
$ filename "IntegerFun.exe"
use rtl64
use corewin
Long Tmpnum , Tmpnum2
Tmpnum = 405
Tmpnum = INT(Tmpnum / 16)
Tmpnum2 = INT( Tmpnum\16 )
print " Tmpnum " + Tmpnum + chr(13,10) + " Tmpnum2 " + Tmpnum2
the results are Tmpnum = 25 and Tmpnum2 = 1
which means that the INT function in O2 is the same as in PB ?
am i right here ?
Hi Chris,
O2 does not have an int() function, like PB's but you won't need one anyway if your variables are integers, and the division is an integer div '\'
For any numeric types, including singles and doubles, we have trunc() and round()
Hi Charles,
What data type do
trunc() and
round() return?
(NB: PB's ROUND() does "banker's", not arithmetic, rounding!)floor() and
ceil() can be imported from
msvcrt.dll.
Quote from: Charles Pegge on May 07, 2018, 07:18:49 AM... you won't need one anyway if your variables are integers, and the division is an integer div '\'
Will
-0.1 \ 1 return
-1 as PB's
INT() does?
Hi Mike,
O2 has core floor() and ceil() too. All of these functions are implemented as FPU macros, and their result is held on the fpu stack (80bit floats) until assigned to a variable. This also applies to whole expressions involving floats, or float division.
-0.1\1 produces -0 :)
Quote from: Charles Pegge on May 07, 2018, 12:58:35 PMAll of these functions are implemented as FPU macros, and their result is held on the fpu stack (80bit floats) until assigned to a variable.
So, Oxygen's
trunc(),
round(),
floor() and
ceil() will automatically match the data type of the variable they are assigned to and thus won't need additional casts?
Quote-0.1\1 produces -0 :)
Sweet! Let's then leave the PB-like INT() and ROUND() (and a hellofalot of other) functional peculiarity re-implementations as a home exercise for whoever is going to be the first one to come up with the PB compatibility layer. :)
These anomalies probably go back to a time when there was a software emulation of the FPU, for those early PCs without this expensive luxury chip (8087) .
Digressing a little further, O2 also supports float infinities, and expressions like float a= 1 / 0 are legit.
If a contains infinity then you will get an accurate 90 degrees from deg atn a
Thanxx Charles
so any division by zero will give infinity ? and the program won't GPF (global protection fault)?
this is a welcome feature for O2
Quote from: Charles Pegge on May 07, 2018, 02:11:55 PM... O2 also supports float infinities, and expressions like float a= 1 / 0 are legit.
This is cool because it makes O2 compatible with GCC. 8)
Chris,
I've just checked:
You can still crash the program with an integer division by zero on an integer:
int i=1\0
A normal division will not crash, but produces a large negative number!
int i=1/0
Only floats have a bit representation for infinity, and handle it correctly, mathematically speaking.
single f=1\0
double f=1\0
single f=1/0
double f=1/0
Mike,
Does GCC support extended precision?
Charles,
Yes, MinGW GCC can support long double/extended precision/80-bit/REAL10 format in calc and formatted output on 32 bits via the compiler command line option -mpc80. Otherwise, it defaults to double and also automatically reduces the long double declarations throughout the code to ordinary 64-bit double pretty much like MS VC would.
On 64 bits, MinGW GCC uses SSE instead.
Under MinGW GCC, (float|double)+|-1.0 / 0 result in +|-INF, respectively, but (int)+|-1 / 0 throw a "division by 0" exception. That's why I noted Oxygen's compatibility with GCC. :)
Charles, do you mean the compiler will do automatic rounding of float integers when storing a float in an integer variable?
If so, that means I can create an int function for the cases when the programmer tries to store an integer value in a DOUBLE
or SINGLE variable, right?
Thanks for the GCC info, Mike.
In 64bit extern mode, O2 is obliged to use SSE registers when passing floats directly, but all its float computations are done in the FPU. It is still possible to pass extended types byref
Hi Brian,
When a float variable or expression is assigned to an integer, it will be rounded up or down. This is not the same as PB's INT() function, which seems to be equivalent to our FLOOR()
http://www.manmrk.net/tutorials/basic/PowerBASIC/pbcc6/int_function.html
I did a test using gcc 7.3 on Windows 10 x64, tested both gcc 32-bit and gcc 64-bit with -mpc80 and without - the result were the same for the respective compiler
//
// test.c
//
//
#include <stdio.h>
int main()
{
union ldb{
unsigned short m[8];
long double ld;
};
typedef union ldb ldbl;
ldbl x;
x.ld=3.1415926535897932384626433832795L;
printf("%24.19Lf\n",x.ld);
printf("%X\n",x.m[0]);
printf("%X\n",x.m[1]);
printf("%X\n",x.m[2]);
printf("%X\n",x.m[3]);
printf("%X\n",x.m[4]);
return 0;
}
gcc 32-bit
gcc test.c -o test
output
-88796093704934495000000000000000000000000000.0000000000000000000
C235
2168
DAA2
C90F
4000
same result with gcc -mpc80 test.c -o test
the result using 64-bit gcc both with and without -mpc80 were
0.0000000000000000000
C235
2168
DAA2
C90F
4000
as can be seen from the output, gcc correctly assigns the long double literal of pi to the variable but fails to print it, however if compiled with -D__USE_MINGW_ANSI_STDIO=1 then it prints the value as expected.
Hi Johan,
Thanks for the feedback. It inspired my own investigation with newer builds of MinGW GCC as my regular tool has always been, and still is, TDM GCC 4.3.3 because:
- among the 64-bit capable GCC compilers, it produces the smallest and fastest executables (up to 2 times smaller and 45% faster) as compared to many later builds I tested my apps against;
- unlike vanilla MinGW, TDM doesn't pull in too much alien Linuxoid stuff and it fits in seamlessly in the Code::Blocks environment that I'm using, as a faithful Windozer, instead of prehistoric console and batch/make file rudiments of the DOS era.
Here are my findings based on the latest available
TDM GCC 5.1.0. I hope they will be useful to public at large because I failed to find all this information elsewhere on the net.
- -mpc80 has never been intended for use under 64 bits, as is clearly stated twice in the gcc.info help file, therefore it is useless to test it in the 64-bit mode.
- Moreover, -mpc80 has never even worked under Windows (at least within the 4.3.3 to 5.1.0 span) though it has not been stressed – I'd say, quite expectedly for a piece of Linux SW – anywhere in the documentation. Thus, older versions like 4.3.3 didn't offer any alternatives at all to control the range of FPU precision other than 32-/64-bit floats.
- Again, even if it would work, it would not be compatible with msvcrt.dll that TDM GCC (and I suppose, vanilla MinGW too) is linked against on default. msvcrt.dll's doesn't even offer a formatted input/output type specifier for anything wider than a double. Therefore, it isn't reasonable even to try to print an EXT or long double via msvcrt.dll, only to get the funny snake
Quote-88796093704934495000000000000000000000000000.0000000000000000000
crawling all around. - Both MS VC and GCC are general-purpose compilers, and there are no ordinary real world phenomena that would potentially require calculation in long-double or even EXT quantities. Arbitrary precision math calc can be done using specialized libraries only when really needed, and it is highly unlikely that NASA or DoD would rely in their multi-billion dollars worth projects on something so prosaic as MS VC, to say nothing of such a ridiculous thing as the restrictive and, at the same time, irresponsible GNU GPL(icense).
- Luckily, later versions including 5.1.0 introduced an alternative option -mlong-double-NN which, much to my surprise and in contrast with the usual Linuxoid practices of irresponsible "as-is"-iness and "absolutely-no-warranty-whatsoever"-ness, does work under Windows and enables us to control, via the NN part of the option, the bitness of FPU calc results stored in GCC's long double variables.
- In many cases, the below compilations throw quite a number of formatting and type mismatch warnings yet complete successfully regardless.
Under 64 bits,
- -mlong-double-64 makes a long double 8 bytes long and thus equal to a VC (long) double compatible with msvcrt.dll:
Quotesize of long double = 8
value = 3.1415926535897931000
It however crashes __USE_MINGW_ANSI_STDIO (see further below for criticism of this yet one more Linuxoid cheat/hack/"crutch"); - -mlong-double-80 makes the long double 16 bytes long with only 80 bits (10 bytes) used for the actual presentation of an EXT precision floating-point quantity. It breaks GCC's compatibility with msvcrt.dll:
Quotesize of long double = 16
value = 0.0000000000000000000
but makes it compatible with the __USE_MINGW_ANSI_STDIO hack:Quotesize of long double = 16
value = 3.1415926535897932385
Note the rounding-up at the 19th decimal place. Also note that there isn't a way to utilize the remaining 6 bytes of storage; a literal value overflows and refuses to compile at a little larger than approx. +1.18973e4932L (this matches well with PB's own 80-bit EXT upper range of approx. +1.2*10^4932 as per the PB manual), or yields GCC's typical +/-INF when overflown as a calculation result. Such behavior is a valid indication that calc indeed takes place in the Intel FPU's 80-bit wide context. - -mlong-double-128 is effectively equal to __float128 and moves the whole stuff to SSE.
- Under 32 bits,
- -mlong-double-64 works the same as it does under 64 bits;
- -mlong-double-80 makes the long double 12 bytes long with only 80 bits (10 bytes) used for the actual presentation of an EXT precision floating-point quantity. It breaks GCC's compatibility with msvcrt.dll:
Quotesize of long double = 12
value = -88796093704934495000000000000000000000000000.0000000000000000000
but makes it compatible with the __USE_MINGW_ANSI_STDIO hack.
- Regarding __USE_MINGW_ANSI_STDIO, it is a dirty hack which links in statically GCC's own re-implementations of formatted input/output functions (printf, sprintf et al.) that introduce Linuxoid format specifiers for long doubles and overrun the respective calls to msvcrt.dll's native procedures. Overall, they
- waste 2 to 6 extra bytes of memory space for every 80-bit literal or variable quantity – persistent or temporary;
- make GCC's long double-aware executables significantly fatter than their GCC-emulated 8-byte (long) double/VC original counterparts; (22KB vs. 6KB minimum for 4.3.3, 35KB vs. 12KB minimum for 5.1.0)
- litter extra space as defined by excessive precision digits with meaningless random noise, e.g.
Quote3.141592653589793238512808959406
for %.30Lf as opposed to MS VC's clear and conciseQuote3.141592653589793100000000000000
for a 64-bit (long) double; - sometimes crash GCC executables where msvcrt.dll would gracefully yield zero-filled output.
________________________________________________
Bottom line, as half baked and clumsy under Windows as much of Linuxoid "multiplatform" software usually is. ;)
In OxygenBasic, all numeric values are passed on the 80bit FPU stack for conversion to Ascii Decimal. It is currently configured to cut off at 17 digits precision, independent of the decimal point position.
in 32bit mode, Extended values can be passed to functions directly, and occupy 12 bytes on the stack. However, in 64bit mode, this is currently not allowed, and the values must be passed by reference.
BTW
I doubt that NASA would require any outputs above single precision, since, in the real world, physical factors at every level, such as multiple gravitational fields, and thermal expansion would out-scale it.
Lisp and its descendants support the notion of precise and imprecise numbers. Floating-point whole numbers and decimal fractions are imprecise while whole integers and common fractions are precise. The n/d common fraction format allows any number to be represented with absolute precision, and it also allows any mathematical operation to be performed (separately on the fraction numerator and denominator) without intermediate conversions and thus without loss in absolute precision.
I'd say half-float precision (that's 3 places after decimal point) is sufficient to describe the real world phenomena decently enough. At least most contemporary game consoles where all the GPU cores support half-float calc natively, are pretty good at rendering animated 3D objects with picture perfect quality and stunning natural looking lighting and physics.
NASA and DoD were mentioned not so much in relation to precision but more from the perspective of reliability and consistency of the language, and the adequacy and self-discipline of its creators.
:)
(IIRC, MASM supports REAL10 and TBYTE descriptors to directly define the 80-bit floating-point precision type and storage size, respectively, and thus doesn't waste a byte of its resources)