It is currently Mon Dec 17, 2018 11:04 am

All times are UTC + 1 hour




Post new topic Reply to topic  [ 25 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Comparing integer and float (or double) variables
PostPosted: Sat May 24, 2014 10:37 pm 
Offline
Addict
Addict
User avatar

Joined: Thu Jun 07, 2007 3:25 pm
Posts: 3307
Location: Berlin, Germany
Hi all,

I recently discovered that it's not possible to compare integer and float (or double) variables reliably -- tested with PB 5.22 LTS x86 and x64 on Windows 7.
Please see the following code:

Code:
Define a.i, b.f, x.f

a = 7
x = 7.4

;----------------

If a < x
   Debug "OK"
Else
   Debug "Huh?"  ; This is shown: wrong
EndIf

;----------------

b = a
If b < x
   Debug "OK"    ; This is shown: correct
Else
   Debug "Huh?"
EndIf

As the code shows, the comparison only works correctly when I first copy the value of the integer variable to another float variable, and then compare both float variables.

Is this by design, or is it a bug?

_________________
Please excuse my flawed English. My native language is PureBasic.
Search
RSBasic's backups


Top
 Profile  
Reply with quote  
 Post subject: Re: Comparing integer and float (or double) variables
PostPosted: Sat May 24, 2014 10:57 pm 
Offline
Addict
Addict
User avatar

Joined: Thu Jan 10, 2008 1:30 pm
Posts: 1169
Location: Germany, Glienicke
The compare sign and if statements use the first parameter for comparison:
Code:
Define a.i = 7
Define x.f = 7.4

Debug "7 < Int(7.4)"
If a < x
   Debug " True"
Else
   Debug " False"
EndIf

Debug "7.4 > Float(7)"
If x > a
   Debug " True"
Else
   Debug " False"
EndIf

I thick it ist no bug, it is a feature

_________________
ImageImage


Last edited by STARGÅTE on Sat May 24, 2014 11:01 pm, edited 2 times in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Comparing integer and float (or double) variables
PostPosted: Sat May 24, 2014 10:58 pm 
Offline
Addict
Addict
User avatar

Joined: Wed Aug 31, 2005 11:09 pm
Posts: 3669
Location: Italy
I think it's a kind of bug-by-design.

Change the a < x to a = x and you will see it will print "OK", that's because the float it's converted to integer (FISTP) so it lose its decimal part.

PB seems to found an integer (a.i) first in the expression, so switches to integer mode for the rest of the expression.

I suppose that's the same reason why you can't use bit shift operators (<< >>) with a perfectly valid integer if there is a float before in the expression.

Code:
; should be perfectly fine !

a.i = 1
b.f =  5.0 * (a << 2) ; 5.0 * 4


So it's by design. Having explicit casts we could at least influence the thing.
Having implicit cast on the other hand should require the compiler to issue warnings from time to time when a possible loss of info can be caused by the implicit conversion, as it happens in your specific example where the decimal part is silently gone.
But not having explicit casts to guide the compiler the warnings could become annoying. Those we have are already annoying to me since you don't have a compiler directive to shut off a specific warning.

Normally in a case like yours the implicit conversion is from integer to float, and not the other way around like PB is doing.

Don't know if this is explained somewhere in the manual BTW, if not it should be... if you know it and pay some (a lot ?) of attention since the compiler doesn't warn you, and since the conversion seem to be just based on a first come first served basis (left to right order), you can avoid it.

_________________
Philosophy is questions that may never be answered. Religion is answers that must never be questioned.

[ My little PureBasic review ]


Top
 Profile  
Reply with quote  
 Post subject: Re: Comparing integer and float (or double) variables
PostPosted: Sun May 25, 2014 7:06 am 
Offline
Addict
Addict
User avatar

Joined: Mon Jul 25, 2005 3:51 pm
Posts: 3510
Location: Utah, USA
As has been stated the left hand side of comparison determines how the right hand side of the comparison will be used.

Because the expression you used on the left hand side of the comparison operator starts with an integer and doesn't include any floating point math or variables it evaluates to an integer and the right hand side will also be converted to an integer to make the comparison.

If you want both sides to be treated as floats then you need to reverse the order of the comparison (i.e x.f > a.i instead of a.i < x.f) or add a float to the first expression (i.e a.i + 0.0 < x.f) or assign the first expression to a float variable before comparison.

Code:
Define a.i, b.f, x.f

a = 7
x = 7.4

;---------------- LHS expression is an integer and so RHS is also converted to integer for comparison

If a < x
   Debug "OK"
Else
   Debug "Huh?"  ; This is shown: correct (the LHS = RHS)
EndIf

;---------------- LHS expression is a float and so RHS is also converted to float for comparison

If x > a
   Debug "OK"    ; This is shown: correct
Else
   Debug "Huh?"
EndIf

;---------------- LHS expression results in a float and so RHS is also converted to a float for comparison

If a + 0.0 < x
   Debug "OK"    ; This is shown: correct
Else
   Debug "Huh?"
EndIf

;---------------- LHS expression is a float and is compared with RHS which is also a float

b = a
If b < x
   Debug "OK"    ; This is shown: correct
Else
   Debug "Huh?"
EndIf

_________________
Image


Top
 Profile  
Reply with quote  
 Post subject: Re: Comparing integer and float (or double) variables
PostPosted: Sun May 25, 2014 7:22 am 
Offline
Addict
Addict
User avatar

Joined: Sat Apr 26, 2003 8:26 am
Posts: 2916
Location: Planet Earth
Demivec wrote:
As has been stated the left hand side of comparison determines how the right hand side of the comparison will be used.

It is a bug. When 2 values are compared, the smaller type is first widened to the bigger type, and
then both are compared. For example, if you compare a Byte and a Quad, the Byte first gets widened
to Quad, and then two quads get compared. It does not matter on which side the Byte stands.
It would not make sense to cut off the Quad to be a Byte, and then compare two Bytes.
Code:
Procedure.b QuadToByte(q.q) : ProcedureReturn q : EndProcedure

Define byte.b = 100
Define quad.q = 256

If QuadToByte(quad) > byte
    Debug "256 is greater than 100"
Else
    Debug "256 is smaller than 100"
EndIf

Instead, the smaller type gets widened to the greater type:
Code:
Procedure.q ByteToQuad(b.b) : ProcedureReturn b : EndProcedure

Define byte.b = 100
Define quad.q = 256

If quad > ByteToQuad(byte)
    Debug "256 is greater than 100"
Else
    Debug "256 is smaller than 100"
EndIf


In this case here, the Float has higher priority than Long, because it can contain more precise values.
The Long needs to get converted to Float, and then 2 Float values get compared.
It does not matter on which side a value stands. With "a.l < b.f" and "b.f > a.l", both times
the Long value needs to get converted to Float first, and then two floats are compared.

6.5.2: Type Conversions

Image


Top
 Profile  
Reply with quote  
 Post subject: Re: Comparing integer and float (or double) variables
PostPosted: Sun May 25, 2014 11:06 am 
Offline
Addict
Addict
User avatar

Joined: Wed Aug 31, 2005 11:09 pm
Posts: 3669
Location: Italy
@danilo

That's how other languages do these things following some kind of logic widely accepted.
In that light, my example of bit shifting should work too, as it does in other languages.
That one has already been marked as a "coding question" and not a bug report (which initially was).

So this can look like a bug to you compared to what it's normally accepted as a solution for the problem, but if fred tell us it is by design, then it's how the thing ends, in a bug-by-design (TM).

When a language it's not strictly defined and almost any behavior involving expressions and such is derived by looking at the forum following the programming by example approach, it's quite difficult to call something a bug.

Could be by "design".

We'll see about this one.

_________________
Philosophy is questions that may never be answered. Religion is answers that must never be questioned.

[ My little PureBasic review ]


Top
 Profile  
Reply with quote  
 Post subject: Re: Comparing integer and float (or double) variables
PostPosted: Sun May 25, 2014 11:33 am 
Offline
Addict
Addict
User avatar

Joined: Mon Jul 25, 2005 3:51 pm
Posts: 3510
Location: Utah, USA
Danilo wrote:
Demivec wrote:
As has been stated the left hand side of comparison determines how the right hand side of the comparison will be used.

It is a bug. When 2 values are compared, the smaller type is first widened to the bigger type, and
then both are compared. For example, if you compare a Byte and a Quad, the Byte first gets widened
to Quad, and then two quads get compared. It does not matter on which side the Byte stands.
It would not make sense to cut off the Quad to be a Byte, and then compare two Bytes.

That rule is true of integers but not of floats and integers. See code below. The code demonstrates that a float is not greater than a long for the reason that it does not hold greater amount of precision. In fact when the max value of a long is assigned to a float the float gets the incorrect value. The example code also shows that a float being at its max precision (that of a long) cannot be incremented by a single unit of precision any more. It doesn't matter what its exponent is, it has the same precision as a long.

Since that is true which should be converted to the other and why?

Code:
Define a.l, b.q, x.f, z.d

a = 2147483646
b = 9223372036854775806
x = a
z = b

Debug "long a: " + a
Debug "quad b: " + b
Debug "(assigned a) float x: " + x + ", incorrect assignment value, not enough precision"
Debug "(assigned b) double z: " + z + ", incorrect assignment value, not enough precision"

Debug "add 1"
a + 1
b + 1
x + 1
y + 1
z + 1
Debug "long a: " + a
Debug "quad b: " + b
Debug "float x: " + x + ", loss of float precision means it can no longer be incremented by 1"
Debug "double z: " + z + ", loss of double precision means it can no longer be incremented by 1"


Image

So I would say that the above diagram is incorrect and that float and long are "mostly equal" and not one above the other and I would say the same is true of quads and doubles.

_________________
Image


Top
 Profile  
Reply with quote  
 Post subject: Re: Comparing integer and float (or double) variables
PostPosted: Sun May 25, 2014 1:42 pm 
Offline
Addict
Addict
User avatar

Joined: Wed Aug 31, 2005 11:09 pm
Posts: 3669
Location: Italy
@Little John

How GNU GCC treat this

Code:
#include <stdio.h>

main()
{
  int a = 7; // int or long
  float x = 7.4;
 
  //; PB in this case follow a left to right rule with no further reasoning about the actual types involved.

  if (a < x) //; implicit cast of 'a' to float
    printf ("7 is < 7.4\n");
  else
    printf ("7 is >= 7.4\n");
   
  a = 1;
  x = 0.0;
 
  //; PB complains here because it sees a float before. What it has to do with the (a << 2) ? Nothing really.

  x =  5.0 * (a << 2);  //; 5.0 * 4 --> 5.0 * 4.0 = 20.0
  printf ("x = %f\n", x);
}



output:

Code:
7 is < 7.4
x = 20.000000


That's the way (uh huh uh huh) I like it (uh huh uh huh)

shorter integer types are converted to longer integer types
shorter float types are converted to longer float types
integers are converted to floating point
signed/unsigned conversions try to avoid loss of data

The validity of the resulting conversions depends on the actual values contained in the variables obviously.

That's why compilers supporting implicit type conversion normally show a lot of warnings when something potentially dangerous is detected and offer explicit casting to let the programmer say to the compiler: "Don't worry, I know in this specific case the range of values stored in the var I'm asking you to coerce into another type will not cause problems."

_________________
Philosophy is questions that may never be answered. Religion is answers that must never be questioned.

[ My little PureBasic review ]


Top
 Profile  
Reply with quote  
 Post subject: Re: Comparing integer and float (or double) variables
PostPosted: Wed May 28, 2014 3:11 pm 
Offline
User
User

Joined: Thu May 11, 2006 9:53 pm
Posts: 97
Quote:
Since that is true which should be converted to the other and why?


When comparing float/double and integer, all types should be internally converted to floating point by the compiler.

The FPU doesn't care what precision your floating point number is. It only deals with EXTended 80 bit floating point numbers. The 32 bit float and 64 bit double only exist in memory, not on the FPU.

When any float/double is loaded onto the FPU it is converted to an internal 80 bit format before being used in any calculation. That conversion is 100% accurate.

That same 80 bit EXT format can fully contain all integers up to 64 bits so any comparison between any float/double and any integer up to 64 bits can be done with the FPU with 100% accuracy.


Top
 Profile  
Reply with quote  
 Post subject: Re: Comparing integer and float (or double) variables
PostPosted: Wed May 28, 2014 3:55 pm 
Offline
Addict
Addict
User avatar

Joined: Mon Jul 25, 2005 3:51 pm
Posts: 3510
Location: Utah, USA
dioxin wrote:
Quote:
Since that is true which should be converted to the other and why?


When comparing float/double and integer, all types should be internally converted to floating point by the compiler.

The FPU doesn't care what precision your floating point number is. It only deals with EXTended 80 bit floating point numbers. The 32 bit float and 64 bit double only exist in memory, not on the FPU.

When any float/double is loaded onto the FPU it is converted to an internal 80 bit format before being used in any calculation. That conversion is 100% accurate.

That same 80 bit EXT format can fully contain all integers up to 64 bits so any comparison between any float/double and any integer up to 64 bits can be done with the FPU with 100% accuracy.

Thank you for sharing that.

That would be great if comparisons were using the 80-bit EXT when making comparisons between floats/dobules and integers because the significand would be able to hold an integer value without loss of precision. Unfortunately it doesn't seem to work that way when variables are involved. The variables are still limited to the precision that they are defined with. Only expressions can make use of the 80-bit EXT format, so it is ineffective in resolving these situations without compromising the comparison (at least in the extremes).
Code:
Define a.i, b.q, x.f, z.d

a = 2147483646
x = a
b = 9223372036854775806
z = b

;----------------
;precision is only kept with both number constants are processed as part of the comparison
;but not when then are stored first in a variable
If 2147483646.0 <= 2147483646
   Debug "OK"   ; This is shown: correct
Else
   Debug "Huh?" 
EndIf

;----------------

If 9223372036854775806.0 <= 9223372036854775806
   Debug "OK"   ; This is shown: correct
Else
   Debug "Huh?" 
 EndIf
 
;----------------

If x <= a
   Debug "OK"
Else
   Debug "Huh?"  ; This is shown: wrong, due to lack of float precision
EndIf

;----------------

If z <= b
   Debug "OK"
Else
   Debug "Huh?"  ; This is shown: wrong, due to lack of double precision
EndIf

_________________
Image


Top
 Profile  
Reply with quote  
 Post subject: Re: Comparing integer and float (or double) variables
PostPosted: Wed May 28, 2014 8:20 pm 
Offline
Addict
Addict
User avatar

Joined: Sat Apr 26, 2003 8:26 am
Posts: 2916
Location: Planet Earth
Demivec wrote:
Unfortunately it doesn't seem to work that way when variables are involved.

Code:
x.f = 2147483646.0
z.d = 9223372036854775806.0

Debug x
Debug z


Top
 Profile  
Reply with quote  
 Post subject: Re: Comparing integer and float (or double) variables
PostPosted: Thu May 29, 2014 2:41 am 
Offline
Addict
Addict
User avatar

Joined: Mon Jul 25, 2005 3:51 pm
Posts: 3510
Location: Utah, USA
Danilo wrote:
Demivec wrote:
Unfortunately it doesn't seem to work that way when variables are involved.

Code:
x.f = 2147483646.0
z.d = 9223372036854775806.0

Debug x
Debug z

See my previous examples (in response to your previous posts) and their explanatory comments.

To sum it up, you can't fix the long to float comparison in all cases by simply converting both sides of the expression to either floats or to longs, they both have limitations. Floats can't represent all long values and longs can't represent all float values. The same is true for quads and doubles.

_________________
Image


Top
 Profile  
Reply with quote  
 Post subject: Re: Comparing integer and float (or double) variables
PostPosted: Thu May 29, 2014 6:17 am 
Offline
Addict
Addict
User avatar

Joined: Sat Apr 26, 2003 8:26 am
Posts: 2916
Location: Planet Earth
Demivec wrote:
To sum it up, you can't fix the long to float comparison in all cases by simply converting both sides of the expression to either floats or to longs, they both have limitations. Floats can't represent all long values and longs can't represent all float values. The same is true for quads and doubles.

You need to make sure that both are within limits, when you compare Float and Long / Double and Quad / Long with Double / Quad and Float.
The original topic is within this limits:
Code:
a.i = 7
x.f = 7.4
Your numbers are not within the limit (as shown by my last posting), so you can't use this numbers to compare Floats with Longs and Doubles with Quads.
Code:
x1.f = 2147483646.0
x2.l = 2147483646

z1.d = 9223372036854775806.0
z2.q = 9223372036854775806

If x1 = x2
    Debug "2147483646.0 = 2147483646"
Else
    Debug "2147483646.0 <> 2147483646"
EndIf

If x2 = x1
    Debug "2147483646 = 2147483646.0"
Else
    Debug "2147483646 <> 2147483646.0"
EndIf


If z1 = z2
    Debug "9223372036854775806.0 = 9223372036854775806"
Else
    Debug "9223372036854775806.0 <> 9223372036854775806"
EndIf

If z2 = z1
    Debug "9223372036854775806 = 9223372036854775806.0"
Else
    Debug "9223372036854775806 <> 9223372036854775806.0"
EndIf


PB does not handle the first case correctly (when numbers are within limits), so it is a bug.
Code:
CompilerIf #PB_Compiler_Processor <> #PB_Processor_x86
    CompilerError "Code for x86 only"
CompilerEndIf

Global a.i, x.f

Procedure DoIt()
    If a < x
       Debug "PB: "+Str(a)+" < "+StrF(x)
    Else
       Debug "PB: "+Str(a)+" >= "+StrF(x)
    EndIf
   
    ;----------------
    DisableDebugger
    ;!FINIT
    !FLD  dword [v_x]
    !FILD dword [v_a]
    !FCOMPP
    !FSTSW AX
    !SAHF
    !JAE _else_1
        EnableDebugger
        Debug "ASM: "+Str(a)+" < "+StrF(x)
        !JMP _endIf_1
    !_else_1:
        EnableDebugger
        Debug "ASM: "+Str(a)+" >= "+StrF(x)
    !_endIf_1:
    Debug "---------------"
EndProcedure

a = 7
x = 7.4

DoIt()

a = 7
x = 7.00001

DoIt()

a = 7
x = 7.00000

DoIt()

a = 7
x = 6.99999

DoIt()

a = 12345
x = 12345.0005

DoIt()
(There are usually more things involved, like setting FPU precision mode etc., but you already know that)

Compiler construction, like mathematics, is taught at universities, and there is plenty of literature available for it.
You seem to think that everybody should invent his/her own mathematics, ignoring all teachings and common standards. It's OK.

Using the common standards, it is correct as long as you stay within the limits of the data types involved. Using PB's own mathematics,
it is almost always wrong or depends on coincidence, like what data types stay on the left and right side of a comparison.

In my example above, staying within limits of both involved data types, 100% of ASM output is correct,
and 40% of PB output is correct for the same comparison. What's better in your world?


Last edited by Danilo on Thu May 29, 2014 7:17 am, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Comparing integer and float (or double) variables
PostPosted: Thu May 29, 2014 7:15 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3268
Location: Netherlands
You can create your own compare procedure based on the code Danilo posted
Code:
Procedure CmpQD(quadValue.q, doubleValue.d)
  !fild qword [p.v_quadValue]
  !fld qword [p.v_doubleValue]
  !fcompp
  !fstsw ax
  !mov cl, ah
  !and cl, 5
  !shr ax, 13
  !and eax, 2
  !or al, cl
  ProcedureReturn
EndProcedure

Quad.q = 5
Double.d = 5

Debug CmpQD(Quad, Double)

Select CmpQD(Quad, Double)
  Case 0:
    Debug "Double > Quad"
  Case 1:
    Debug "Quad > Double"
  Case 2:
    Debug "Equal"
  Case 7:
    Debug "Error"
EndSelect

_________________
macOS 10.14 Mojave, PB 5.62 x64


Top
 Profile  
Reply with quote  
 Post subject: Re: Comparing integer and float (or double) variables
PostPosted: Thu May 29, 2014 8:24 am 
Offline
Addict
Addict
User avatar

Joined: Mon Jul 25, 2005 3:51 pm
Posts: 3510
Location: Utah, USA
@Danilo: I appreciate your input (even if it is leaning towards being condescending :) ). My observations are meant to show that despite what has been said there isn't a simple tried-and-true way to do this. All of the ways involve limitations because they don't work for every situation.

Danilo wrote:
Using the common standards, it is correct as long as you stay within the limits of the data types involved. Using PB's own mathematics,
it is almost always wrong or depends on coincidence, like what data types stay on the left and right side of a comparison.


It is good you brought up common standards. What are the common standards for PureBasic?

The only description I found in an official reference was in the help manual:
Help file under 'General Rules' wrote:
Expressions

An expression is something which can be evaluated. An expression can mix any variables, constants, or functions, of the same type. When you use numbers in an expression, you can add the Keyword $ sign in front of it to mean a hexadecimal number, or a Keyword % sign to mean a binary number. Without either of those, the number will be treated as decimal. Strings must be enclosed by inverted commas.


That seems to say that comparisons of different types is already inviting trouble. An easy solution to this is to convert the types to a common type. Suggestions to do this have included using the function Int() or Round(), instigating automatic conversion by adding 0.0, and using assembly to invoke the FPU.

Danilo wrote:
What's better in your world?

I'm using PureBasic too. There is information regarding observations about how PureBasic works through an expression with mixed types (i.e. blueznl's PureBasic Survival Guide). These and the help file's advice to not mix types should guide someone in how they construct the comparisons.

Everything else involves Fred changing or improving PureBasic. I think he should detail some more about the standards so that these questions might be dealt with more easily. I think that the suggestions regarding type casts would be helpful. Other hypothetical suggestions such as promoting everything to 80-bit precision extended floats when they are being so that they will 'always' be accurate will have the drawback of possibly slowing things down unnecessarily.

Danilo wrote:
Compiler construction, like mathematics, is taught at universities, and there is plenty of literature available for it.
You seem to think that everybody should invent his/her own mathematics, ignoring all teachings and common standards. It's OK.

There are standards and there is plenty of literature available. All the ones I found have said the standards vary according to what is deemed most important. You can do methods A, B, or C. If you choose A you get some results; B gives some of the results of A and a few others; C will give you some of the results of B but not all of A's. There is nothing that is 'best' or 'perfect' since it is dealing with the limitations imposed by the computer, time, or money.


@wilbert: Wouldn't this code function just as well?
Code:
Procedure CmpQD(quadValue.q, doubleValue.d)
  Protected x.d, result
  x = doubleValue - quadValue
  If x = NaN() Or x = IsInfinity()
    result = 7
  Else
    Select Sign(x)
      Case 1 : result = 0 
      Case -1: result = 1
      Case 0 : result = 2
    EndSelect
  EndIf
 
  ProcedureReturn result
EndProcedure

_________________
Image


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 25 posts ]  Go to page 1, 2  Next

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye