Double precision is off?

I have had the same scheme for my app checking a server to see if there’s an update for over 10 years. It recently stopped working correctly.

I have a text file on server with version. My app periodically checks for new version by comparing itself to what’s in file. Pretty simple…

// s is string retrieved, in this case “2.35” which matches the major, minor, bug version 2,3,5
// both remoteVersion and myVersion are declared as doubles and then compared like so:

remoteVersion = val(left(s, 4))
 myVersion = (app.MajorVersion+app.MinorVersion/10+app.BugVersion/100)

 if myVersion < remoteVersion then.... //open a dialog to advise

 // in debugger I see:
    myVersion = 2.3499999999999996
    remoteVersion =2.3500000000000001

If I change to single (in lieu of using double) I get 2.3499999046325684 for both, which is fine, since it doesn’t misinform my user that they have an out of date app.

Better to use integer arithmetic for this. Convert your 2,3,5 into 235 and then compare against that.

1 Like

You can use Double.Equals to mitigate the issue, but as Tim says, Double is really the wrong variable type to use for this. You have simply been lucky the last 10 years.

1 Like

‘val’ returns a double, which is why I used doubles.

The documentation of ‘val’ shows:
Var n As Double
n = Val(“12345”) // returns 12345
n = Val(“54.25car45”) // returns 54.25
n = Val(“123.25”) // returns 123.25
n = Val(“123 25”) // returns 123
n = Val(“123,456”) // returns 123
n = Val(“auto”) // returns 0
n = Val(“&hFFF”) // returns 4095
n = Val(“&b1111”) // returns 15

Var s As String
s = “12345”
n = s.Val // returns 12345

A Double cannot represent many (most?) fractional values exactly. And as you have discovered, they will not use the same approximation, depending on how you create them. So why not just compare string values instead? You get a string from the server, so create a string from your major, minor, bug versions.

1 Like

I can figure out other ways to do it and am aware of the difference between doubles, singles, etc, I am just bringing it up because it seems odd that something that was worked going back to RealBasic of 2007 or so, suddenly stopped working with Xojo 2022 v. 3. I searched the forum for something similar and didn’t see it, so thought it was useful to report. Also found it interesting that if using single, both approximations are equal. Maybe this will be useful to someone else…

Appreciate the input.

Take my criticism with all the best intentions, but your scheme is not the best designed one. People store much more digits than 1 for minor and 1 for build/bug. And no one uses floating for such, but…

Here is a proposal to keep the current compatibility:

Var s As string = "2.35" // s receives version as M.IB string (M=Major from 0 to 999, I=Minor 0-9, B=Bug 0-9

If app.MinorVersion>9 Or app.BugVersion>9 Then
  MessageBox "Bad version digits. Check them."  // Dev error
End

Var remoteIntegerVersion As Integer = s.Trim.Val*100

Var thisIntegerVersion As Integer = app.MajorVersion*100+app.MinorVersion*10+app.BugVersion

If thisIntegerVersion < remoteIntegerVersion Then //open a dialog to advise
  Messagebox("Need to update")
End

This is a really bad idea, any of those 3 numbers can become a double or triple digits and your old versions will become unable to compare.

Yes, double precision sucks for this kind of use and xojo dont have Decimal numbers (currency is limited and uses doubles in conversions), doubles are aproximations to the value so it can have many rounding problems.

I have the same system as you, with a text file (Json) with the major, minor and bug versions then asigning them to INTEGER variables and comparing those, first the Major then Minor and Bug…

If not Json, you can parse the version string and still assign the parts to Integer variables for comparing

It is more code but it is future proof, working with any digit size of the versions and dont having the xojo rounding problems in doubles.

I just tried your code in 2019r1.1 and it showed the same issue so I don’t believe its a recent update that’s caused the problem. Maybe you have just been lucky that the two values have landed on similar results all this time?

This should work around the issue:

myVersion = app.MajorVersion + CType(app.MinorVersion/10 + app.BugVersion/100, Double)

It’s possible that they switched to a different library, or that some OS upgrade changed it. Either way, it isn’t something that they have a lot of control over.

For this specific example, yes. It doesn’t mean another example will fail. What you’ve done is just reduce the precision of the floating portion. It doesn’t prevent the type of error you encountered. Either use the Equals method or find a better way to compare. It could be as simple as

if Round(myVersion*100) = Round(remoteVersion*100) then

But as others have pointed out, you may want to provide for version numbers that are greater than a single digit. I would go for a string version formated like “002.003.005”. Maybe even 4 digits per value.

I realize that that introduces an incompatibility with your existing installations. You could add an additional value after left(s,4) or you could just store this idea for new projects.

First time you encountered a value which exhibited the problem, is all.
It would have happened even if you had always used 2007

1 Like

You are correct. I just mocked up a test using RB 2012 I have on an old machine and I see the same thing. Ha ha. Curiously, if I use 2.34 instead of 2.35, both #'s are the same.

If you make your string be mmm.nnn.bbb with any number of digits in each portion, you can get:

Var  maj, min, bug as Integer, verStr as String

verStr = "2.3333.77"

maj = verStr.NthField(".", 1).ToInteger
min = verStr.NthField(".", 2).ToInteger
bug = verStr.NthField(".", 3).ToInteger

// and then do your comparison

I just tested this and it works OK.

Thx TimS - used a similar scheme which allows me to have b/w compatibility with what’s up there now, but just test integers. Thanks to others for their input.