Bug in MetaEditor Build 3566: Wrong display of double floating point numbers in the debugger window

 

I am attaching a screenshot of the error.

Open test.mq5 in MetaEditor.

Put a breakpoint on line #6, then run the debugger.


The string() function bug was fixed before in build 3210.

Wrong display of double floating point numbers in Dialog boxes and output of Print(), Alert(), Comment() and FileWrite() functions.

Report: https://www.mql5.com/en/forum/367839/page3#comment_27477157

Fix: https://www.mql5.com/en/forum/367839/page5#comment_27613205 and https://www.metatrader5.com/en/releasenotes/terminal/2226


However, the debugger window of MetaEditor have NOT been fixed yet, as you see in the screenshot.

The debugger must have very precise output with numbers, in order to not to confuse programmers un-aware of this bug.

Actually, the debugger window displays the longest round-trip string, while, Print() and input dialog boxes display the shortest round-trip string. Both are accurate, but the longest-round trip form is confusing and not intuitive.

MT5/mql5 reported and confirmed bugs. - Custom ZERO Level Showing ZERO level, bug in MML. String(MQ)
MT5/mql5 reported and confirmed bugs. - Custom ZERO Level Showing ZERO level, bug in MML. String(MQ)
  • 2022.02.01
  • www.mql5.com
On indicators, mt5 can set automatically a level "0" depending of the buffer values. Custom indicator showing zero level, though not defined anywhere in the code. Wrong conversion of double -> string. Then test the same example in your browser js pad or any online javascript pad and check the difference
Files:
test.mq5  1 kb
 

Floating-point has an infinite number of decimals, it's you, not understanding floating-point and that some numbers can't be represented exactly. (like 1/10.)
          Double-precision floating-point format - Wikipedia

See also The == operand. - MQL4 programming forum (2013)

If you want to see the correct number of digits, convert it to a string with the correct/wanted accuracy.
          question about decima of marketinfo() - MQL4 programming forum (2016)

 
William Roeder #:

Floating-point has an infinite number of decimals, it's you, not understanding floating-point and that some numbers can't be represented exactly. (like 1/10.)
          Double-precision floating-point format - Wikipedia

See also The == operand. - MQL4 programming forum (2013)

If you want to see the correct number of digits, convert it to a string with the correct/wanted accuracy.
          question about decima of marketinfo() - MQL4 programming forum (2016)

Enough using copy and paste!

Try to use something else, please.

 

If two numbers are equal, then you should choose the visualization of the number with the least number of digits.

string GetCloneNumber( const double Num )
{
  string Str = (string)Num;
  int Count = 1;
  
  while (Num != (double)(Str + IntegerToString(1, Count, '0')))
    Count++;  

  Str += IntegerToString(1, Count, '0');
  
  return(Str);
}

void DoubleProperty( const double Num )
{
  const string Str = GetCloneNumber(Num);
  const double Num2 = (double)Str;
  
  Print((string)Num + " == " + Str + " - " + (string)(Num == Num2));
}

void OnStart()
{
  DoubleProperty(99999999.95242);
  DoubleProperty(0.95242);
  
  Print(99999999.95242 == 99999999.952420001); // true
  Print(0.95242 == 0.95242000000000001);       // true
}
This rule does not work in the debugger yet.
 
amrali #:

Enough using copy and paste!

Try to use something else, please.

  1. Why should I rewrite my answer? What part of my post, that answered your post, was wrong.

  2. You may think my posts are Floccinaucinihilipilification, but not everyone thinks like you. (2022)

 
@William Roeder # What part of my post, that answered your post, was wrong.

What @amrali is referring to, is that in previous builds, MetaQuotes improved the display of floating point numbers by rounding/normalising/whatever-you-wish-to-call-it, so as to improve the visual appearance of these numbers in the terminal and debugger. There was however, a build in the past in which this "feature" failed and all the floating point numbers appeared will multiple digits, just like it is happening now in this particular build.

@amrali is aware how floating point numbers work and also about the rounding/normalising issues and has posted quite extensively about it.

What he is asking of you William, is that you consider the context of the issue and the person asking about it, and not just copy/paste a generic answer. Even though your answer was technically correct, it was not really addressing the issue.

 
fxsaber #:

If two numbers are equal, then you should choose the visualization of the number with the least number of digits.

This rule does not work in the debugger yet.
Yes exactly
 
Dear William
You replied with 3 statements with neither no relations to each other, nor the issue that am I addressing here. I have not printed or compared anything, please refer to my first post

Thanks 😊 
 
William Roeder #
Floccinaucinihilipilification, 
I had to look that one up 😁

 
Paul Anscombe #:
I had to look that one up 😁

nice 😂 😇


 

I will elaborate a little on @fxsaber answer to emphasize an important feature of the IEEE-754 floating-point format.

Because fp format has a limited number of bits (64 for double, 32 for float), while the real numbers line is infinite, therefore many real numbers (close enough) are actually get encoded to the same binary (hex) value. 

As in this case, the numbers 0.95242, 0.952420000000000044, 0.952419999999999998 and many other very close numbers (within a half epsilon, above and below) are encoded as 0x3FEE7A398201CD60 in binary (i.e., many-to-one encoding), and this is unlike the one-to-one encoding of integers. The format guarantees a precise binary representation, for any decimal number as long as it has 17 significant digits or less (the precision). Significant digits or figures is the sum of integer and decimal digits (left and right of the decimal point), excluding leading and trailing zeros. For example, 0.95242,12.345, 9981.2 all have 5 significant figures. But, 1100 has only two sf.

The computer does not know anything about the string representations (0.95242), it sees only the binary. It is the job of the software to convert (translate) that binary into a human readable form.

The software is free to choose which string to display as long as it is round-trips back to the same binary.

So, to display 0x3FEE7A398201CD60 to the user, we can choose the string "0.952419999999999998(the longest round-trip  string) or the string "0.95242(the shortest round-trip). Both are valid and correct results.

But, if the software converts the above binary to the string 0.9524200000000002 then this means an error, because this string round-trips to the binary  0x3FEE7A398201CD61, which is a different representable fp number.

So, for an optimal display of floating-point numbers:

  1. The software must output a string that round-trips back to the same numeric value (accurate conversion: binary -> string -> binary).
  2. It is preferable (NOT a must) to display the shortest round-trippable string.

Number 2, is now a standard in other programming languages like python, C# and JavaScript. They adopt the shortest round-trip string conversion.

Edit:

Before build 3210, MetaQuotes did not conform to rule #1. Both MT4 and MT5 had wrong conversions of double -> string. For example Print(0.95242) => 0.9524200000000002.

But, still they have some issues with rule #2 in build 3566. The display of decimal strings should be unified all over the software (i.e., to be consistent with either the longest or the shortest form).

This is a demo script to display the above numbers in binaries and strings:

#include <math_utils.mqh>  // https://www.mql5.com/en/code/20822

#define PRINT(A) Print(#A + " = ", (A))

void OnStart()
  {
   double d = 0.95242;
   Print(d);

   // examine our fp number
   PRINT(0.95242);                                           // 0.95242            (what we see is the string representation)
   PRINT(DoubleToHexadecimal(0.95242));                      // 0x3FEE7A398201CD60 (what the computer see is the binary representation)

   // In real world, we have infinite real numbers very close to 0.95242 (+/- half epsilon).
   // How to encode them provided that we have only 64 bits ?
   // They are encoded to the same hex value (i.e., many-to-one encoding), and this is unlike the one-to-one encoding of integers.
   PRINT(DoubleToHexadecimal(0.952419999999999989));         // 0x3FEE7A398201CD60
   PRINT(DoubleToHexadecimal(0.952419999999999998));         // 0x3FEE7A398201CD60
   PRINT(DoubleToHexadecimal(0.95242));                      // 0x3FEE7A398201CD60
   PRINT(DoubleToHexadecimal(0.952420000000000022));         // 0x3FEE7A398201CD60
   PRINT(DoubleToHexadecimal(0.952420000000000033));         // 0x3FEE7A398201CD60
   PRINT(DoubleToHexadecimal(0.952420000000000044));         // 0x3FEE7A398201CD60

   // because these two numbers are encoded to the same binary
   PRINT(0.95242 == 0.952420000000000044);                   // true

   // let's advance to the next representable fp number
   PRINT(NextAfter(0.95242));                                // 0.9524200000000002
   PRINT(DoubleToHexadecimal(NextAfter(0.95242)));           // 0x3FEE7A398201CD61

   // because these two numbers differ in binary by 1 bit (ulp, unit in the last place)
   // so, they must not be equal (the equal operator == compares the binaries).
   PRINT(0.95242 == 0.9524200000000002);                     // false
  }


Edit:

This function displays the accurate + shortest string for doubles.

MetaQuotes have fixed their double -> string conversion routines starting from build 3210

//+------------------------------------------------------------------+
//| Converting numeric value into the shortest string representation |
//| that round-trips into the same numeric value. The result will    |
//| contain at most 17 significant digits, discarding trailing zeros.|
//| The round-trip ("%.17g") format specifier ensures that a numeric |
//| value converted to a string is always parsed back into the same  |
//| numeric value, StringToDouble(Repr(f)) == f.                     |
//| Note: results are consistent with David M. Gay's dtoa.c library. |
//+------------------------------------------------------------------+
// toString()
string Repr(const double value)
  {
//--- https://stackoverflow.com/a/35708911/4208440
//--- https://www.exploringbinary.com/number-of-digits-required-for-round-trip-conversions/
//--- Try format with 15, 16, 17 significant digits to return the shortest
//--- decimal numeric string which round-trips to the specified value.
   string str = NULL;
   for(int sig = 15; sig <= 17; sig++)
      if(value == StringToDouble(str = StringFormat("%.*g", sig, value)))
         break;
   return str;
  }

void OnStart()
  {
   PRINT(Repr(0.952419999999999998));   // 0.95242
   PRINT(0.952419999999999998);         // 0.95242 
  
   PRINT(Repr(0.95242));                // 0.95242
   PRINT(0.95242);                      // 0.95242  (fixed in build 3210)
  }


Still, we can print the longest round-trip string, if we would like to:

void OnStart()
  {
//--- the "g" format specifier refers to si[g]nificant digits.    
   PRINT(StringFormat("%.17g", 0.95242));  // 0.95242000000000004  (the longest round-trip)
  }

So, it all depends on the software implementation that decides which string to show to the user.

Reason: