【问题标题】:Counting digits in a float计算浮点数中的数字
【发布时间】:2011-01-10 05:07:28
【问题描述】:

我正在学习一些 C++ 中的 OpenGL 初学者教程,但是当我开始作为一名 C# 程序员时,它让我认为很多事情是理所当然的。所以当我调试将我的 FPS 读数打印到输出时出现了我的问题。我认为该方法类似于我头顶上的 DebugPrintString,它需要一个 char* 并且基本上我正在打印“FPS:x”。我使用 scanf_s 将 fps 值放入字符数组中,但这是我的问题所在。字符数组必须有多大?

让我再详细说明一下:我的 FPS 读数存储为浮点数,因为帧/秒通常最终不是一个好数字。所以我的号码可能是 60,也可能是 59.12345。 60 只需要 2 个字节,而 59.12345 需要 8 个(周期为 1)。所以我想“哦,好吧,我需要计算它的位数,没问题!”男孩让我感到震惊。

我做了一个计算数字的方法,计算小数点的左侧很容易,首先将它转换为 int 以删除小数点并除以 10(实际上我认为我有一些位移那里)并计算我可以这样做的次数,直到我达到0。现在计算右侧的数字,我将乘以10,减去数字,然后一直这样做直到它达到零。我认为该方法通常会返回 32。所以我 WTF'd 并在调试中查看了它,结果当你将浮点数相乘时有效地将数字列向上移动,因为众所周知的精度问题它只是附加了另一个数字!

我做了一些主要的谷歌搜索,但在 char str[128] 和 scanf 之上找不到任何内容,如果在 then 中执行 strlen(str) 减 1(空终止符)。但我希望有一个更优雅的解决方案。最后我只是将它转换为一个 int 并允许 9999 fps,还添加了一个检查以查看 fps > 9999 但我认为这不会发生。比 SEG FAULT 更安全:(

TLDR:有没有办法获得浮点数的位数? scanf 是怎么做到的?!

抱歉发了这么长的帖子,只是想分享一下我的挫败感>:D

编辑:拼写错误

【问题讨论】:

  • sprintf?这句话是填充文本。
  • 抱歉什么意思?时间太长了?是的哈哈对不起:P
  • “递归”需要在他的评论中至少写 15 个字符。 “冲刺?”只有八岁。
  • 这可能是使用这 7 个字符最令人困惑的方式。

标签: c++ floating-point floating-accuracy frame-rate


【解决方案1】:

每秒帧数。只用左边三位,右边一位,谁在乎浮点表示碰巧认为十位以下存在什么?

编辑以回应第一条评论。

http://en.wikipedia.org/wiki/Floating_point

如果我们真的想知道浮点数的位数,我们就必须知道浮点数是如何工作的。 IEEE 标准浮点数据类型中表示的确切值具有一定数量的位,通常为 32 或 64,这些位以标准化方式分配,为您提供一定数量的有效数字,以及一些幂指数来按比例放大或缩小。 24 位有效数字大约为小数点后七位。当然,最后总会出现某种形式的截断或舍入错误(例如,用十进制写的三分之一,当你停止写重复的三时总是向下舍入)。

也许您的号码在七八位数字后停止,但机器舍入错误使其意外运行?读取那种数据是没有意义的。

【讨论】:

  • 是的,你的权利,我真的不关心任何小数位,但它可能有一天会再次出现,所以我想我不妨调查一下。
【解决方案2】:

CashCommons 的 John 给出了传统的答案,只需使用 printf 格式限定符来强制一定宽度。我通常发现 2 位小数对于帧速率来说已经足够了。它可以让您区分 30 fps 和 29.97 fps 这两个最常见的值。

 sprintf (buffer, "%.2f", fps);

但是,如果您想知道最坏的情况是什么,也有办法知道这一点。结帐http://en.wikipedia.org/wiki/IEEE_754-2008

它表明浮点值(32 位浮点数)在尾数中有 24 个二进制数字,计算结果为 7.225 个十进制数字,称为 8。添加 5 位为指数,1 位为符号,1 位为前导0, 1 为小数点,你得到

 1 + 1 + 8 + 5 = 15 characters

为终止空值添加空间,您将获得 16 位数字。

【讨论】:

    【解决方案3】:

    由于浮点数没有以任何精确的方式存储,因此实际上没有任何有效的方法来计算位数。您可能想要做的是控制浮点数提供的长度并使用固定大小的缓冲区。

    通过printf()scanf()等相关函数,可以通过修改格式类型说明符来指定浮点数的大小。虽然简单的%f 可能是最常见的,但可以通过在%f 之间添加修饰符来更灵活地使用它,例如:

    %[width][.precision]f
    

    其中[width] 是指最小 显示的数字位数(如果超过则不会截断),[.precision] 指定要显示的确切位数小数点后。

    例如,您可以检查以下程序的输出:

    #include <cstdio>
    
    using std::printf;
    int main() {
      float f(59.12345);
    
      printf("[%f]\n", f);    // [59.123451]
      printf("[%15f]\n", f);  // [      59.123451]
      printf("[%1f]\n", f);   // [59.123451]
      printf("[%.2f]\n", f);  // [59.12]
      printf("[%6.2f]\n", f); // [ 59.12]
      printf("[%4.1f]\n", f); // [59.1]
    }
    

    字符数组的确切大小仍然会根据小数点前的值而变化。但是只要可以控制宽度和精度,将足够的内存分配到字符数组中(或将数字放入固定长度的数组中)应该会简单得多。

    【讨论】:

      【解决方案4】:

      考虑到我们说的是 C++,为什么不走 STL 方式呢?

      精确到小数点后5位,可以是可变数量的字符:

      std::stringstream ss;
      ss << std::setprecision (5) << std::fixed << f; 
      std::string fps = ss.str();
      

      精度最大5位有效数字:

      std::stringstream ss;
      ss << std::setprecision (5) << f; 
      std::string fps = ss.str();
      

      【讨论】:

      • 哦,当然,让我们都看中我们。 ;) (+1 顺便说一句)
      • 很酷,感谢您提供替代解决方案 :) 我从来没有真正接触过 std::string,我只是一个初学者,我从一些 C 教程开始。我想我从来没有摆脱 char* 的习惯,哈哈。
      • @daniel - 是时候这样做了,你会节省很多时间和精力:>
      • 这样做的真正好处是你不需要知道你的fps字符串有多大。当然,这也比你以前做的要慢得多......
      • 我不会说慢很多。肯定更慢,但不是瓶颈慢。在真正的游戏中,它会非常快,带有内存管理器。
      【解决方案5】:

      您可以使用 sprintf 将浮点数截断/强制为所需的任何精度。然后问题就消失了。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-03-22
      • 1970-01-01
      • 1970-01-01
      • 2021-09-01
      • 1970-01-01
      • 2014-03-12
      • 1970-01-01
      相关资源
      最近更新 更多