QB64.org Forum

Active Forums => QB64 Discussion => Topic started by: Ryster on February 22, 2020, 04:54:26 pm

Title: QB64x64 math differences from QB64x32
Post by: Ryster on February 22, 2020, 04:54:26 pm
Hello
Maybe someone met with erroneous mathematical calculations in QB64x64. Namely, I have a program that in QBx64 displays the wrong result, while in QB64x32 the same identical program displays correctly.

Regards - Ryster
Title: Re: QB64x64 math differences from QB64x32
Post by: FellippeHeitor on February 22, 2020, 05:25:02 pm
You forgot to attach the program. Also to explain the issue.
Title: Re: Re: QB64x64 math differences from QB64x32
Post by: Ryster on February 22, 2020, 05:31:26 pm
Code: QB64: [Select]
  1. J = 4381911: A = 52
  2. B# = 4 * (J + A + 1401) + 3
  3. G = 5 * INT((B# MOD 1461) / 4) + 2
  4. D = INT((G MOD 153) / 5) + 1
  5. M = ((INT(G / 153) + 2) MOD 12) + 1
  6. PRINT D; M
Title: Re: Re: QB64x64 math differences from QB64x32
Post by: FellippeHeitor on February 22, 2020, 05:37:19 pm
I see the different values for D in the 32bit version and in the 64bit version. It probably boils down to how math with floating point numbers is done differently in the two architectures.

Math experts may come along and help shed some light to the issue. Especially given your complex math there, which probably also plays a part.
Title: Re: QB64x64 math differences from QB64x32
Post by: FellippeHeitor on February 22, 2020, 05:41:08 pm
I can safely add that you merging SINGLE and DOUBLE variable types is playing a part in the problem as well.

Please add DEFDBL A-Z to the top of that sample code you provided (or DIM all variables AS DOUBLE) and you will see that calculations will perform identically, regardless of it being 32bit or 64bit.
Title: Re: QB64x64 math differences from QB64x32
Post by: Ryster on February 22, 2020, 06:16:13 pm
Thank you.
But it doesn't seem to me that combining double precision numbers with a single number must result in an error. I have over a thousand such patterns in the code with various variable designations and it will take a lot of time to correct it all. I hope that one of the QB64x64 experts will improve this shortcoming. Below is the code that counts correctly.

J# = 4381911: A# = 52
B# = 4 * (J# + A# + 1401) + 3
G = 5 * INT((B# MOD 1461) / 4) + 2
D = INT((G MOD 153) / 5) + 1
M = ((INT(G / 153) + 2) MOD 12) + 1
PRINT D; M
Title: Re: QB64x64 math differences from QB64x32
Post by: FellippeHeitor on February 22, 2020, 06:18:52 pm
Again, this requires understanding of how computers handle floating point math, and you'd better adapt your code now that you've found the issue.

QB64 is just instructing the computer to calculate. And that does vary according to architecture.

Issue solved as far as I'm concerned.
Title: Re: QB64x64 math differences from QB64x32
Post by: Ryster on February 22, 2020, 06:25:34 pm
And for me it is not solved.
It cannot be that in a 32-bit 2 + 2 = 4, and in a 64-bit 2 + 2 = 5
Title: Re: QB64x64 math differences from QB64x32
Post by: FellippeHeitor on February 22, 2020, 06:29:06 pm
Uparty Ryster,

If you tell me that 2 + 2 results in something different according to architecture, then I'll eat my words.

Now please come back to this topic after reading and understanding the links below:

https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html (https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html)
https://stackoverflow.com/questions/31415712/floating-point-differences-between-64-bit-and-32-bit-with-round (https://stackoverflow.com/questions/31415712/floating-point-differences-between-64-bit-and-32-bit-with-round)
Title: Re: QB64x64 math differences from QB64x32
Post by: SMcNeill on February 22, 2020, 06:40:48 pm
What's probably happening is that the 32-bit build is using the 80-bit FPU registers to do the calculation and the 64-bit build is using the SIMD operations using 64-bit values, causing a slight discrepancy. Note that both answers agree to 14 decimal places, which is about the best you can hope for with 64-bit floating point values.

There are several compiler flags which you can set to modify precision operations (I posted them in another post similar to this one; I’ll see if I can find you the link in a bit), which may help with your specific system architecture.  We don’t set any of those flags by default, as we need to keep things working for the plurality of systems possible, but you can try them out and see what might work best for your own specific needs.

Try these compiler options here: https://www.qb64.org/forum/index.php?topic=1798.msg113933#msg113933
Title: Re: QB64x64 math differences from QB64x32
Post by: SMcNeill on February 22, 2020, 07:05:24 pm
I’ll post a quote from stack exchange where someone had an answer to a similar issue:

Quote
GCC can also generate instructions that use the old x87 FPU, but when generating x86-64 code, the default is to use SSE2. On Mac OS X, the default is to use SSE2 even in 32-bit since all Intel Macs have SSE2. When it generates instruction for the 387, GCC does not set the precision of the FPU to the double format, so that computations are made in the 80-bit double-extended format, and then rounded to double when assigned.

As a consequence:

If you use only double computations, Visual Studio should generate a program that computes exactly at the precision of the type, because it is always double(**). And if on the GCC side you use -msse2 -mfpmath=sse, you can expect GCC to also generate code that computes at the precision of doubles, this time by using SSE2 instructions. The computations should match.
Or if you make both GCC and Visual Studio emit SSE2 instructions, again, the computations should match. I am not familiar with Visual Studio but the switch may be /arch:SSE2.
This does not solve the problem with math libraries, which is indeed an unsolved problem. If your computations involve trigonometric or other functions, you must use the same library as part of your project on both sides. I would recommend CRlibm. Less accurate libraries are fine too as long as it's the same library, and it respects the above constraints (using only double or compiled with SSE2 on both sides).

(*) There may be a way to instruct it to generate SSE2 instructions. If you find it, use it: it will solve your particular problem.

(**) modulo exceptions for infinities and subnormals.

As this guy indicates; the solution may be just as simple as setting a compiler specific flag on your system.  (Probably the -msse2 -mfpmath=sse option in the 64-bit version.)
Title: Re: QB64x64 math differences from QB64x32
Post by: luke on February 23, 2020, 04:29:08 am
You are performing calculations that are solely in the domain of integers, since your divisions are always rounded (to negative infinity).

Thus your choice of floating-point variables to hold these results is a poor one.

Prefer an integer type, like so:
Code: [Select]
DEFLNG A-Z

J = 4381911
A = 52
B = 4 * (J + A + 1401) + 3
G = 5 * INT((B MOD 1461) / 4) + 2
D = INT((G MOD 153) / 5) + 1
M = ((INT(G / 153) + 2) MOD 12) + 1
PRINT D; M
Title: Re: QB64x64 math differences from QB64x32
Post by: Ryster on February 23, 2020, 06:41:23 am
Can you correct the code - displays the error in the first line.
Title: Re: QB64x64 math differences from QB64x32
Post by: FellippeHeitor on February 23, 2020, 08:33:53 am
Can you correct the code - displays the error in the first line.

The code does work perfectly, Uparty Ryster.

  [ This attachment cannot be displayed inline in 'Print Page' view ]

Your problem is something else at this point.
Title: Re: QB64x64 math differences from QB64x32
Post by: Ryster on February 23, 2020, 08:46:24 am
To FellippeHeitor
I see you have a lot to say in this Forum. Therefore, I agree to delete my profile from qb64.org/forum.
Goodbye
Title: Re: QB64x64 math differences from QB64x32
Post by: SMcNeill on February 23, 2020, 08:48:13 am
To FellippeHeitor
I see you have a lot to say in this Forum. Therefore, I agree to delete my profile from qb64.org/forum.
Goodbye

Bye.  /wave

Just curious though: What error does DEFLNG A-Z generate???
Title: Re: QB64x64 math differences from QB64x32
Post by: Dimster on February 23, 2020, 10:50:50 am
Sorry to see Ryster go. I'm world's worst programmer in that I have very little understanding sometimes as to why somethings work and why some don't work. The issue Ryster raised I have come across before - clueless as to why it worked in Qbasic and not QB64x64, so just threw different things at it until I found the Round function solved my problem. I was hoping this thread would give some more insight and walla the "specific system architecture" was raised.

So is that referring to the OS of Windows, Apple, Android etc, or Hardware installed in a computer, or is that referring to how QB64 deals with 32 bit values or 64 bit values applying the same operation (ie 10 x 354 is handled differently if using QB64x32 v's using QB64x64).

Title: Re: QB64x64 math differences from QB64x32
Post by: bplus on February 23, 2020, 11:13:18 am
Hi Dimster,

I'm no tech genius either but floating point math is part of Program Language compiling code so if there were problems I would look there before the OS which the PL does have to work with.

I too have found surprises, what I thought should be 0 wasn't exactly according to say an IF evaluation because of junk wandering in from big precision float math.
Title: Re: QB64x64 math differences from QB64x32
Post by: Qwerkey on February 23, 2020, 02:32:28 pm
Therefore, I agree to delete my profile from qb64.org/forum.
Goodbye

That has got to be the most clearly illustrative example of cutting off one's nose to spite one's face.
Title: Re: QB64x64 math differences from QB64x32
Post by: SMcNeill on February 23, 2020, 02:35:04 pm
Sorry to see Ryster go. I'm world's worst programmer in that I have very little understanding sometimes as to why somethings work and why some don't work. The issue Ryster raised I have come across before - clueless as to why it worked in Qbasic and not QB64x64, so just threw different things at it until I found the Round function solved my problem. I was hoping this thread would give some more insight and walla the "specific system architecture" was raised.

So is that referring to the OS of Windows, Apple, Android etc, or Hardware installed in a computer, or is that referring to how QB64 deals with 32 bit values or 64 bit values applying the same operation (ie 10 x 354 is handled differently if using QB64x32 v's using QB64x64).

At the end of the day, QB64 just translates BAS code to C code.   mingw is the compiler we use to then compile that C code to an EXE.

GENERALLY SPEAKING:  *
G++ 32-bit uses the 80-bit precision X87 FPU math processors by default.
G++ 64-bit uses 64-bit precision SSE2 math processors by default, as they’re much faster.

That gives us a noticeable difference in results as the precision limits are different.  Usually this is a difference of something like 0.000000002 or such, and it’s hardly noticeable — BUT when rounding it can cause a huge change in values.

INT(15.9999999999999999) = 15
INT(16.0000000000000001) = 16

Only .0000000000000002 difference in those values, but their INT value is quite different.



* You notice I mentioned GENERALLY SPEAKING above??  That’s because various machines and OSes have different architecture that they default to.  From what I’ve heard, Mac OS X and up all use 64-bit SSE2 processing — even on 32-bit Macs...

If one wants to alter these type of default behaviors, they usually just need to set the proper flags to tell the compiler ”I want the slower, 80-bit FPU math, rather than the faster 64-bit SSE2 math”.


Title: Re: QB64x64 math differences from QB64x32
Post by: FellippeHeitor on February 23, 2020, 02:35:08 pm
@Qwerkey thanks for teaching me an expression! Had never heard that one. :-)
Title: Re: QB64x64 math differences from QB64x32
Post by: Qwerkey on February 23, 2020, 02:39:22 pm
@Fellippe, it is a very odd expression indeed and very rarely used, but I thought that I'd foist it upon everyone.
Title: Re: QB64x64 math differences from QB64x32
Post by: SMcNeill on February 23, 2020, 02:40:38 pm
A few quick wiki links to help:

https://en.wikipedia.org/wiki/X87

https://en.wikipedia.org/wiki/SSE2

Quote
Differences between x87 FPU and SSE2
FPU (x87) instructions provide higher precision by calculating intermediate results with 80 bits of precision, by default, to minimise roundoff error in numerically unstable algorithms (see IEEE 754 design rationale and references therein). However, the x87 FPU is a scalar unit only whereas SSE2 can process a small vector of operands in parallel.

If codes designed for x87 are ported to the lower precision double precision SSE2 floating point, certain combinations of math operations or input datasets can result in measurable numerical deviation, which can be an issue in reproducible scientific computations, e.g. if the calculation results must be compared against results generated from a different machine architecture. A related issue is that, historically, language standards and compilers had been inconsistent in their handling of the x87 80-bit registers implementing double extended precision variables, compared with the double and single precision formats implemented in SSE2: the rounding of extended precision intermediate values to double precision variables was not fully defined and was dependent on implementation details such as when registers were spilled to memory.
Title: Re: QB64x64 math differences from QB64x32
Post by: luke on February 23, 2020, 05:02:27 pm
Also, https://floating-point-gui.de/
Title: Re: QB64x64 math differences from QB64x32
Post by: MWheatley on February 24, 2020, 12:17:09 pm
@Fellippe, it is a very odd expression indeed and very rarely used, but I thought that I'd foist it upon everyone.

British, I think.

Malcolm
Title: Re: QB64x64 math differences from QB64x32
Post by: bplus on February 24, 2020, 01:23:29 pm
Rhinectomy - cutting off all nosy decimals.

Title: Re: QB64x64 math differences from QB64x32
Post by: romichess on February 24, 2020, 04:24:22 pm
The reason this kind of problem exist is because values on the computer are digital and not analog. That means that there are values that are just simply impossible to represent inside a computer. The error is bigger for single precision floating point than for double precision numbers. The point would be then that if the 64 bit mingw compiler uses different size registers or a slightly different format a different size error would be propagated through the calculations producing a slightly different answer. If absolute precision out to a number of decimal places that C++ cannot handle is required there are languages that can handle much larger values. Some languages can string together hundreds of bytes to form any precision needed. Though they tend to be very slow. Don't hold me to this but Euphoria 4.0 is I believe one of those languages. Euphoria 4.0 is both an interpreter and an .e to .c compiler.