You are missing trading opportunities:
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
Registration
Log in
You agree to website policy and terms of use
If you do not have an account, please register
This a test script to reproduce the bug:
I have some time to spare :-D
Example of a "fail" :
Original d1 = 1.0054400000000001
Temp string = "1.00544" // DoubleToString(d1, digits)
New d1 = 1.0054399999999999 // StringToDouble(DoubleToString(d1, digits))
d2 = 1.0054400000000001 // NormalizeDouble(d1, digits)
The documentation says nothing about the rounding scheme used by NormalizeDouble. Why are you thinking the problem is NormalizeDouble (it could be but we have no idea about it) when StringToDouble give 1.0054399999999999 instead of 1.0054400000000001 ?
The problem could be in StringToDouble or both. There could also be no problem at all except some "incoherence", a function using one possible representation and one the other.
What the standard says about the binary representation to use for a number with 2 possible ones ?
OK, Alain let me explain this a bit.
First, you start with a rounded decimal string "1.00544" as if it is human typed. To parse this string to a machine number, StringToDouble() converts it to the nearest binary approximation as a 64-bit floating point value.
To check if conversion string -> double is correct, we can examine it here https://baseconvert.com/ieee-754-floating-point
So, StringToDouble() is Ok. A more automated way to the check correctness of StringToDouble() is thru round-tripping string -> double -> string. as long as these functions StringToDouble() and string() is ok, the round-trip will succeed in both directions. This is the case now after string() was fixed. StringToDouble() in mql is always correct in all versions.
Now, this is our rounded machine number in 64-bit hexadecimal notation:
What happens, is that NormalizeDouble() instead of giving back the same number, it introduces sometimes a round-off error in the rightmost bit.
NormalizeDouble(3FF0164840E1719F) = 3FF0164840E171A0, which is incorrect. As you see one ULP (bit) is added, and this is a round-off error.
Therefore you get this un-rounded result 1.0054400000000001 from the rounded input number.
Using the converter in the above link.
A more formal methodology is that NormalizeDouble() to be cross-checked against a more robust function, like .Net round() that uses a 128-bit decimal type:
https://docs.microsoft.com/en-us/dotnet/api/system.decimal.round?view=netframework-4.8
cross_check.mq5
I hope I have explained the bug, clearly. It is a minor bug though!
Thanks
Edit:
Try other values in the above script like 1.00543 to check.
OK, Alain let me explain this a bit.
First, you start with a rounded decimal string "1.00544" as if it is human typed. To parse this string to a machine number, StringToDouble() converts it to the nearest binary approximation of the 64-bit floating point value.
To check if conversion string -> double is correct, we can examine it here https://baseconvert.com/ieee-754-floating-point
A more automated way to the check correctness of StringToDouble() is thru round-tripping string -> double -> string. as long as these functions StringToDouble() and string() is ok, the round-trip will succeed in both directions. This is the case now after string() was fixed. StringToDouble() in mql is always correct in all versions.
This is our rounded machine number in 64-bit hexadecimal notation:
What happens, is that NormalizeDouble() instead of giving this same number, it introduces a round-off error in the rightmost bit.
NormalizeDouble(3FF0164840E1719F) -> 3FF0164840E171A0 as you see one ULP (bit) is added, and this is a round-off error.
Therefore you get this un-rounded result 1.0054400000000001
A more formal methodology is that NormalizeDouble to be cross-checked against a more robust function, like .Net round() that uses a 128-bit decimal type:
https://docs.microsoft.com/en-us/dotnet/api/system.decimal.round?view=netframework-4.8
cross_check.mq5
It's not obvious to me why 3FF0164840E1719F is the most accurate representation of "1.00544", why not 3FF0164840E171A0 ?
Is this defined in the standard ?
It's not obvious to me why 3FF0164840E1719F is the most accurate representation of "1.00544", why not 3FF0164840E171A0 ?
Is this defined in the standard ?
Yes sure, it is the IEEE 754 Floating Point standard that all CPU manufacturers follow. Something like the ASCII and UTF standards for representation of strings on computers
Edit:
Try also 1.0054400000000001 to see its hex.
FYI, this bug in NormalizeDouble() has zero impact on rounding the prices of orders as they are sent to trading sever, because Metaquotes servers set some higher tolerance much more > 1 bit (ULP), to consider the price received as not rounded to be rejected.
That is way no major issues have occurred, except that every now and then you see some posts on the forum reporting strange issues with rounding.
Yes sure, it is the IEEE 754 Floating Point standard that all CPU manufacturers follow. Something like the ASCII and UTF standards for representation of strings on computers
Edit:
Try also 1.0054400000000001 to see its hex.
I know the hex representation. I am just saying I don't see why one value is more accurate than the other. Apparently Metaquotes doesn't know either ;-)
I reported it as bug, so when you see some strange results, do not bother, it is minor and it will not affect your orders in any way. If this occurs in a scientific or mathematical program, then it will be a major flaw.
Why would you want to use NormalizeDouble() in a scientific or mathematical program ?
Small details in floating-point calculations makes difference.
Let's say, you divide a floating-point number by 100, so you code it as number / 100.0 (floating-point division).
But also, you can write the above expression as number * 0.01 (using multiplication with the reciprocal of 100). This latter method is way faster but it gives a round-off error.
This is for demonstration only (not production code):