【问题标题】:How to ensure that floating-point variables entered with 2 decimal places retain their exact value如何确保以 2 位小数输入的浮点变量保持其精确值
【发布时间】:2016-07-25 19:10:02
【问题描述】:

有什么办法可以确保用户输入的浮点变量,小数点后有 2 位小数,保持其精确值,而不是失去精度?

这是一个例子: 我想在小数点后用 50 个数字四舍五入 float,像这样

四舍五入前 = 0.70999997854232788085937500000000000000000000000000

到这里:

四舍五入后 = 0.71000000000000000000000000000000000000000000000000

当我想在这样的条件下比较 float 号码时,我感到困惑:

== 程序开始==
输入:0.71

/* program logic */
if (input == 0.71) {
    printf("True !");     
} else {
    printf("False !");
}

输出:错误!
==程序结束==

输出是False !,并且永远是False !,因为用户输入的真实值是0.70999997854232788085937500000000000000000000000000,而不是0.71000000000000000000000000000000000000000000000000

有没有办法像这样对float 值进行舍入?我在这里读到了浮点数不准确的可能性:

但是,这些并不能回答我的问题。如果我使用ceilf(input * 100) / 100 函数,当printf()d 使用%.5f 格式时,它将使输入0.71 变为0.71000 - 这似乎有效。但是当我使用%.50f 打印时,实际输入值显示为0.709999978542327880859375。所以,在我的情况下,我无法与那个值进行比较。

那么,如果浮点数永远不可能准确,那么上面的程序逻辑在该条件下获得true 返回值的技巧是什么?

【问题讨论】:

  • 您可以安全地从问题中删除所有这些尾随零。除了使问题变得混乱之外,他们什么都不提供。
  • 一个float的值甚至不能保留50位小数:double只能保留大约16位。请read this
  • Rounding Number to 2 Decimal Places in C - 你提到了这个和其他保证的重复,但没有解释你为什么忽略它们,即为什么你认为他们没有解决这个问题。跨度>
  • 无论如何,浮点总是准确到特定范围;对于所有数字,它并不总是精确。这些都是定义明确的概念。

标签: c floating-point rounding


【解决方案1】:

所有用户的输入都以文本的形式出现。检查它的最简单(可能也是最安全)的方法是在转换为数字之前将其作为字符串进行比较:

if (strcmp("0.71", input) == 0) {
    printf("True !");
} else {
    printf("False !");
}

当然,如果您想检查除相等之外的其他内容,您可能无法接受:)

另一点是您对固定感兴趣,而不是这里的浮点数——但会将它们与实际整数混淆。如果您想要使用百分之几,那么使用百分之一的整数...例如,考虑金钱——而不是使用float(或double)来存储 美元 的金额(例如 7.61 美元),使用 int(或者,上帝保佑你,long)来存储 美分(例如 ¢761)。 p>

同样,再举一个例子,如果您计算时间并且需要千分之一秒的精度,那么请使用整数毫秒数,而不是浮点数秒数。

这将完全回避您的整个问题,还可能使您的程序更快...

【讨论】:

  • 您知道这样的方法会增加巨大的性能开销吗?
  • 其实恰恰相反……整数通常比浮点数快。
  • 我的意思是strcmp 方法
  • 他在谈论用户输入,它以文本形式出现。 strcmp 比将文本转换为数字要快得多——然后您仍然需要将转换结果与您自己的数字进行比较。
  • 我对这个答案很感兴趣,我正在寻找这个,将 float 与另一个技巧进行比较:D,但我必须测试它,然后才能将您的答案标记为真实答案:D(y)
【解决方案2】:

我确信这个问题之前已经回答过,但是:

float rounded_down = floorf(input * 100) / 100;   /* Result: 0.70 */
float nearest = roundf(input * 100) / 100;  /* Result: 0.71 */
float rounded_up = ceilf(input * 100) / 100; /* Result: 0.71 */

我认为这个函数所属的库是 math.h。

【讨论】:

  • 我的英语也很抱歉,希望你能理解。无论如何,我发布的代码必须适用于任何情况。
  • 我正在编辑我的问题,请再读一遍,如果我错了,我很抱歉证明您从该问​​答线程中的回答不能满足我的问题:)
  • 这本身并不能提供问题的答案,因为你还没有解释你应该用它做什么。此外,. 70. 71 不能精确地表示为二进制浮点数,因此 rounded_down 实际上将类似于 . 699999988079071044921875,等等。
【解决方案3】:

您可以使用您所体验过的范围来测试相等性。 只需重写你的相等测试:

if(input <= .71 + EPSILON && input >= .71 - EPSILON) {
}

在这里,您可以根据自己的容差创建自己的 EPSILON,或者您可以使用 C++ 中 #include&lt;cfloat&gt; 或 C 中 #include &lt;float.h&gt; 中的 FLT_EPSILON。

回想一下,浮点数没有 50 位小数精度。因此,尝试在小数点后 50 位进行四舍五入不会很好。浮点数大约有 6 位(有时更多)的精度。

【讨论】:

  • 当然,在现实中,人们会将这样的测试纳入一个可重用的函数中,该函数采用比较值(可能还有调用时间的 epsilon 等)
  • 当然可以。这是一个应该可以工作的代码 sn-p:int equal_floats(float first, float second, float EPSILON) { return first &lt;= second+EPSILON &amp;&amp; first &gt;= second-EPSILON;},然后,这个调用将是 if(equal_floats(input,.71,0.0001)) 或类似的东西。根据您的版本,您可能会考虑在函数原型中也将 int 更改为 bool。
  • 那是#include &lt;float.h&gt;,因为这是一个C问题。
猜你喜欢
  • 2015-11-12
  • 1970-01-01
  • 2021-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-23
相关资源
最近更新 更多