程序库: 数学工具 - 页 5

 
FXAI #:
这是三个方便的函数,用于浮点数的比较和四舍五入以及货币格式化:

`bool DoubleEquals(double x, double y, double eps)` 用给定的epsilon值`eps`比较两个双数值`x`和`y`,并返回一个布尔值,表示它们是否在给定的公差范围内相等。

`double RoundTo(double value, int digits)` 将一个 double 值 `value` 四舍五入到给定的小数位数 `digits`。

3.`string FormatMoney(double amount)` 将 double 值 `amount` 格式化为表示货币金额的字符串。它将金额格式化为两位小数,用逗号替换小数点,并每隔三位数字插入空格以提高可读性。它还会在末尾添加从 `AccountInfoString(ACCOUNT_CURRENCY)` 中获得的货币符号。

非常感谢。不过,库中已经实现了这些函数(甚至比您的结果更强大),只是名称不同而已。

// 检查两个数字是否相等,精确到 "n "位。
int    Compare(const double a, const double b, const int digits);
bool   EqualDoubles(double a, double b, int significantDigits = 15);
bool   IsClose(const double a, const double b, const int maxDifferentSignificantDigits = 2)

// 精确的小数四舍五入以避免意外结果。
double Round(const double v);                       
double Round(const double value, const int digits); 
double Round(const double value, const double step);

// 用千位分隔符和指定的小数格式化双倍值。
string FormatDouble(const double number, const int digits, const string separator=",");

 

你好 @amrali,感谢您的贡献。

也许这是一个错误?

我希望第二次打印的结果是 "0.0001"。

如果是错误,如何修复?如果不是,我的代码有什么问题?

谢谢。

double ask = 1.2973;
double bid = 1.2972;
double spread = ask - bid;

Print(spread);// 输出:0.00009999999999998899
Print(StripError(spread));// 输出:0.000099999999999989


amrali
amrali
  • 2024.04.05
  • www.mql5.com
Trader's profile
 
jonlinper #:

你好 @amrali,感谢您的贡献。

也许这是一个错误?

我希望第二次打印的结果是 "0.0001"。

如果是错误,如何修复?如果不是,我的代码有什么问题?

谢谢。


打印十六进制表示法,你就会明白,差值与真正的实际值 0.0001 相差甚远(这是因为在减法过程中出现了舍入误差)。

因此,您必须使用四舍五入程序。

   double ask = 1.2973;
   double bid = 1.2972;
   double spread = ask - bid;

   Print(spread);                                  // 输出:0.00009999999999998899
   Print(StripError(spread));                      // 输出:0.000099999999999989

   Print(DoubleToHexadecimal(spread));             // 输出:0x3F1A36E2EB1C4000
   Print(DoubleToHexadecimal(StripError(spread))); // 输出:0x3F1A36E2EB1C4001
   Print(DoubleToHexadecimal(0.0001));             // 输出:0x3F1A36E2EB1C432D

   Print(EQ(spread, 0.0001));                      // 输出: true
   Print(Round(spread, 16));                       // 输出:0.001

你应该注意到一些细微的差别:

StripError() 在第 16 个有效数字 0.00009999999999998899 时进行四舍五入(0 不计算在内)。

Round(x, 16)小数点 后第 16 位数字 0.000099999999998899 进行四舍五入。

 
jonlinper #: 我希望第二次打印的结果是 "0.0001"。

浮点数有无数个小数。是 不理解浮点运算,不理解有些数字无法精确表示。(比如 1/10。)
双精度浮点格式 - 维基百科

另请参阅== 操作数。-MQL4 编程论坛 (2013)

如果您想看到正确的位数,请将其转换为具有正确/想要的精度的字符串。
关于 marketinfo() 的 decima 的问题 - MQL4 编程论坛 (2016 )

 
William Roeder #:

浮点数有无数个小数。是 不理解浮点运算,不理解有些数字无法精确表示。(如 1/10。)
双精度浮点格式 - 维基百科

另请参阅== 操作数。-MQL4 编程论坛 (2013)

如果您想查看正确的位数,请将其转换为具有正确/所需精度的字符串。
关于 marketinfo() 的 decima 的问题 - MQL4 编程论坛 (2016 )

亲爱的 William,感谢您的说明,但我不同意您关于 "无限小数位数 "的观点。FP 数字的小数位数实际上是有限的。(例如 0.1 的小数点后正好有 52 位数字)。

请使用我的库中的DoubleToStringExact(0.1) 来打印它们。此外,您还可以使用下面的计算器查看完整的小数字符串: https://www.exploringbinary.com/floating-point-converter/
另外,请注意完整的小数字符串必须以数字 "5 "结尾。

0.1000000000000000055511151231257827021181583404541015625
 

只打印两位数有效数字的最佳方法是什么?

double Trunc(const double value, const int digits);

这个函数对 99.9% 的数字都很有效,但对 1.0000000000 这样的整数 就有问题。

我的问题是,我需要去掉非有效数字,但由于某些原因,我无法只使用 @Trunc、

所以我最终使用了

string Normalize_Double_ToString(double n, int d)
{
   // 第 1 步--有助于排除尾部的零
   n = Round(n, d);

   // 第 2 步 - 计算有效小数的个数
   int sd = GetSignificantDecimals(n);

   // 第 3 步--我们不需要超过 @d 中指定的数量
   if (sd > d){ sd = d; }

   // 第 4 步 - 去除不需要的小数,不进行负随机四舍五入 
   double t = Trunc(n, sd);

   // 调试
   //PrintFormat("%s [%d] [%d] :: %s", DoubleToString(n, DBL_DIG), d, sd, DoubleToString(t, sd));

   // 第 5 步 - 设置精度
   string s = DoubleToString(t, sd);

   return s;
}

它能完全满足需要,为我提供所有数字的最小字符串,但我想知道是否可以对它进行优化,以便在处理 1.00000000 这样的整数时仍能得到最小字符串。

谢谢

 

刚刚意识到我正在使用

int GetSignificantDecimals(double value)

略作修改的版本

int GetSignificantDigits(double value)

代码

int GetSignificantDecimals(double value)
{
   if(!value || !MathIsValidNumber(value))
   {
      return 0;
   }

   // 小数之和
   int digits = GetDigits(value);

   // 不包括尾部的零
   while(MathMod(value, 10) == 0)
   {
      digits--;
   }

   return digits;
}
 
Cristian Dan Fechete 整数 就有问题。

我的问题是,我需要去掉非有效数字,但由于某种原因,我无法只使用 @Trunc、

所以我最终使用了类似

它能完全满足需要,为我提供所有数字的最小字符串,但我想知道是否可以对它进行优化,使它在处理 1.00000000 这样的整数时仍能得到最小字符串。

谢谢

对不起,您需要了解什么是有效数字,因为我发现您的代码混淆了基本概念。
请用简单的语言解释,不要用代码。请举例说明您发现的问题以及您的期望。
 
amrali #:
对不起,您需要了解什么是有效数字,因为我发现您的代码混淆了基本概念。
请用简单的语言解释,不要用代码。请举例说明您发现的问题以及您的期望。

感谢您抽出时间,是的,我并不完全确定我理解了 "有效数字"。

我基本上需要 "打印 "尽可能短的数字。例如

1.0000000 -> 1

1.0090000 -> 1.009

123.00100 -> 123.001

对我来说,"重要位数 "是指:去掉后会改变数字值的位数,因此尾数零并不重要。


顺便说一下,自从 Windows 上次更新后,函数 Round(double, int) 导致 MT4 阻塞。我发布的第一段代码工作正常,但从昨天晚上开始,它完全冻结了 MT4 客户端。

 
Cristian Dan Fechete #:

感谢您抽出宝贵的时间,是的,我不太明白 "有效数字 "的含义。

我基本上需要 "打印 "尽可能短的数字。例如

1.0000000 -> 1

1.0090000 -> 1.009

123.00100 -> 123.001

对我来说,"重要位数 "是指:去掉后会改变数字值的位数,因此尾数零并不重要。


顺便说一下,自从 Windows 上次更新后,函数 Round(double, int) 导致 MT4 阻塞。我发布的第一段代码工作正常,但从昨天晚上开始,它完全冻结了 MT4 客户端。

Print() 函数或将 double 转换为字符串,如 (string)dbl 将获得尽可能短的有效数字,无需先操作数字。这是 MQL 的内置功能。(我已经向开发团队提出了修复建议,并将其合并到代码中)。

您需要做的就是
string num_str = string(number).
或 Print(number);

这就是为什么库中不需要一个专门的函数来将数字打印或格式化为尽可能短的字符串,MQL 语言已经支持该功能。

只有在需要控制小数点后的位数时,才使用 DoubleToString() 函数。如果参数 digits 大于数字中的小数位数,返回的字符串将附加 0,例如
DoubleToString(1.09, 5) 返回字符串 "1.09000"。

如果参数的位数小于数字的小数位数,数字将被近似处理,例如 DoubleToString(1.12345, 2) 返回字符串 "1.12"。您的困惑是由于无法区分数字和字符串。

请注意,以零结尾的数字,如 1.09、1.090、1.0900、1.09000 和 1.090000,在变量中存储为相同的双精度 fp 数。这些数字只能由用户作为手动输入直接输入。在程序中,所有这些数字都存储为同一个数字,即 1.09,尾数 0 不存储。

double a = 1.09;
double b = 1.090000;
Print(a); // "1.09" (1.09)
Print(b); // "1.09"

四舍五入函数,如 round、ceil 和 floor,可以将输入数改变(近似)为另一个数,即小数点后具有指定小数位数的最接近的双数,或者在 RoundToSignificantDigits() 中包含指定有效位数总数的数。

希望以上内容能消除大家对将双精度 fp 数转换为字符串的困惑。