Page 1 of 2

ValF("2.3") sometimes returns "2.0"

Posted: Tue Sep 13, 2011 11:18 am
by c4s
This is probably the most mysterious error/bug(?) I've ever encountered. Under certain circumstances ValF() seems to return wrong values. Rough example code:

Code: Select all

Global Dim SArray.s(200)
SArray(10) = "2.3"

Global SGlobal.s
SGlobal = ""


Procedure Change()
	SGlobal = "Something"
EndProcedure

Procedure Test()
	Debug SArray(10)  ; Always "2.3"
	Debug ValF(SArray(10))  ; Always "2.0" instead of "2.29..." when SGlobal gets changed
EndProcedure



;Change()
Test()
Unfortunately the code above doesn't reproduce the error but it's approximately how it looks like. All I can say is that the array values never change and that the code above does not run in a thread. Also SGlobal always changes before Test() (as it isn't in a thread)...
I compiled it with PB 4.51 32bit, Unicode and Threadsafe enabled. It produces the error on Windows Vista (Dual Core). However I can't reproduce it on my developing PC (Windows XP, Single Core).

So what I'm asking is: Under which circumstances can ValF() produce wrong results?

Re: ValF() sometimes returns "2.0" instead of "2.29..."

Posted: Tue Sep 13, 2011 2:17 pm
by Zach
Sideline: I thought Floats were inherently inaccurate ?

I doubt this is a bug - as I found this pretty easily through Google

http://cboard.cprogramming.com/c-progra ... uracy.html

Re: ValF("2.3") sometimes returns "2.0"

Posted: Tue Sep 13, 2011 2:19 pm
by c4s
Yes, they are. However e.g. ValF("2.3") should (and usually does) return "2.29...". However in my case it returns "2.00...".


Edit:
What I forgot to say: I compiled the executable months ago and the error seems to occur since just a few weeks (Windows Update?!).

Re: ValF("2.3") sometimes returns "2.0"

Posted: Tue Sep 13, 2011 2:37 pm
by jboadas
In my machine VafF("2.3") returns 2.29999995231628
but ValF("2,3") returns 2.0
check if this have something to do with international configuration of your machine

Re: ValF("2.3") sometimes returns "2.0"

Posted: Tue Sep 13, 2011 2:41 pm
by c4s
Well, the value always has a point (-> ValF("2.3")). But interesting to know that it produces different results. Does it depend on system settings?!

I updated my previous post: The error seems to appear since only a few weeks and as I already said just when the other global string is set. Extremely strange... :|

Re: ValF("2.3") sometimes returns "2.0"

Posted: Tue Sep 13, 2011 3:06 pm
by MachineCode
jboadas wrote:but ValF("2,3") returns 2.0
check if this have something to do with international configuration of your machine
Sounds right. The decimal separator can be . or , depending on your locale. If it's . and you pass a number with , as the separator, then of course ValF() is going to ignore all digits after the , character. Perfectly normal for it to do so.

Re: ValF("2.3") sometimes returns "2.0"

Posted: Tue Sep 13, 2011 3:22 pm
by DarkDragon
MachineCode wrote:
jboadas wrote:but ValF("2,3") returns 2.0
check if this have something to do with international configuration of your machine
Sounds right. The decimal separator can be . or , depending on your locale. If it's . and you pass a number with , as the separator, then of course ValF() is going to ignore all digits after the , character. Perfectly normal for it to do so.
It usually doesn't depend on your locale, it is fixed in english notation since the first PB version. Locale handling is much more complex and shouldn't be put in such a simple data type conversion function.

Re: ValF("2.3") sometimes returns "2.0"

Posted: Tue Sep 13, 2011 4:10 pm
by c4s
@MachineCode
Even if this would be the issue, it wouldn't explain why ValF() works perfectly fine when that other string isn't set.

Re: ValF("2.3") sometimes returns "2.0"

Posted: Tue Sep 13, 2011 8:43 pm
by Guimauve
I have tested the code in LinuxMint 11 x64 witn PureBasic V4.60 RC1 x64 and both test return :
2.3
2.29999995231628
So the problem occur only on Windows, if this can help someone to understand what is really going on.

Best Regards.
Guimauve

Re: ValF("2.3") sometimes returns "2.0"

Posted: Wed Sep 14, 2011 12:46 am
by citystate
you say that the code you supplied doesn't reproduce the error?

in the code that DOES, are you accidentally casting to a long or integer?

Code: Select all

a.l=ValF("2.3")
b.i=ValF("2.3")
c.f=ValF("2.3")

Debug ValF("2.3")
Debug a
Debug b
Debug c

Re: ValF("2.3") sometimes returns "2.0"

Posted: Wed Sep 14, 2011 9:00 am
by c4s
@citystate
No, I'm using something like:

Code: Select all

WriteFloat(FileNr, ValF(GArray(10)))
Where...
- FileNr is a valid, opened file
- GArray(10) is the global array I was talking about in the first post. The value is "2.3"
- ValF() returns "2.29..." and under certain circumstances "2.0"
- WriteFloat() writes "2.0". It's definitely not the problem here.

Re: ValF("2.3") sometimes returns "2.0"

Posted: Wed Sep 14, 2011 2:25 pm
by Little John
Without code that reproduces the problem, only some guesswork is possible.

Regards, Little John

Re: ValF("2.3") sometimes returns "2.0"

Posted: Wed Sep 14, 2011 2:48 pm
by c4s
@Little John
Of course. But I was wondering if someone with internal knowledge of PureBasic could tell me what ValF() itself does. Maybe it isn't threadsafe or it interferes with the internal string manager or whatever...

Re: ValF("2.3") sometimes returns "2.0"

Posted: Wed Sep 14, 2011 7:02 pm
by SFSxOI
Returns 2.29999995231628 here on Windows 7 Ultimate x86, it is represented within standards requirements correctly. Don't confuse precision with representation.

Part of it is how your CPU does floating point representations, even the compiler may choose different floating point representation methods. From what I discovered about C++ (in which I am certainly no expert), the rest I think is explained by how windows stores the mantissa. The mantissa is stored as a binary fraction and has a value greater than or equal to 1 but less than 2. Because this bit is always 1, it is assumed in either the real*4 (8 bit) and real*8 (11 bit) formats which have biases of 127 and 1023 . The binary (not decimal) point is assumed to be just to the right of the leading 1, and PureBasic is also the same way (I think) and the help alludes to this (a little bit) where it says "Another limitation of floating point numbers is that they still work in binary, so they can only store numbers exactly which can be made up of multiples and divisions of 2." . Most compilers have options that let you indicate how you want to work with floating point operations, generally faster is less accurate and more accurate is slower. It will behave differently on different operating systems, its one of the inherent "flaws" in using floating point numbers in that there is no gurantee it will behave the same all the time, I try to stay away from working with them myself unless I am using multiples of 2 and then I generally round. If you need more accuracy, consider doubles instead of floats. Plus your number (2.3) is not a multiple of 2, numbers that are a multiple of 2 are more accurate for floats, for example:

Code: Select all

Debug ValF("2.4") = 2.40000009536743
and
Debug ValF("2.3") = 2.29999995231628

you can see the difference between one which is a multiple of 2 (the 2.4) and the other which is not a multiple of 2 (the 2.3)

The Purebasic help says this:

"Special information about Floats and Doubles

A floating point number is stored in a way that makes the binary point "float" around the number, so that it is possible to store very large numbers or very small numbers. However, you cannot store very large numbers with very high accuracy (big and small numbers at the same time, so to speak).

Another limitation of floating point numbers is that they still work in binary, so they can only store numbers exactly which can be made up of multiples and divisions of 2. This is especially important to realise when you try to print a floating point number in a human readable form (or when performing operations on that float) - storing numbers like 0.5 or 0.125 is easy because they are divisions of 2. Storing numbers such as 0.11 are more difficult and may be stored as a number such as 0.10999999. You can try to display to only a limited range of digits, but do not be surprised if the number displays different from what you would expect!

This applies to floating point numbers in general, not just those in PureBasic.

Like the name says the doubles have double-precision (64 bit) compared to the single-precision of the floats (32 bit). So if you need more accurate results with floating point numbers use doubles instead of floats.

The exact range of values, which can be used with floats and doubles to get correct results from arithmetic operations, looks as follows:
Float: +- 1.175494e-38 till +- 3.402823e+38
Double: +- 2.2250738585072013e-308 till +- 1.7976931348623157e+308"

If you take a look at the IEEE standard at http://en.wikipedia.org/wiki/IEEE_754 you will find that the representation your seeing meets the standard requirements, so although it may not be as precise as you wish the representation is correct according to the standard.

Re: ValF("2.3") sometimes returns "2.0"

Posted: Wed Sep 14, 2011 7:57 pm
by c4s
@SFSxOI
Thanks for this detailed information about floats but I think you didn't realize that I'm not talking about very small differences like 0.0001.
I didn't test it yet (as I can't reproduce it on my developing computer) but I think everything after the point gets truncated in the ValF() function under certain conditions. It really doesn't make any sense! Again something simple to illustrate my problem:

Code: Select all

Debug ValF("2.3")  ; Returns 2.29999995231628

; Do some simple stuff such as changing strings etc. (see first post)
Debug ValF("2.3")  ; Returns 2.0
Tomorrow I'll hopefully be able to test a little more and be able to give some real reproducible code.