开发者

Counting digits in a float

I am following some开发者_JAVA百科 beginner tutorials for OpenGL in c++ but as I started off as a c# programmer it has made me take a lot of things for granted. So my problem occurred when I was debug printing my FPS reading to the output. I think the method was something like DebugPrintString off the top of my head which took a char* and basically i was printing "FPS: x". I was using scanf_s to place the fps value into the character array but this is where my problem lies. How big does the character array have to be?

Let me elaborate a bit more: my FPS reading is stored as a float as the frames/seconds usually ends up not being a nice number. So my number could be 60, or it could be 59.12345. 60 would only need 2 bytes and 59.12345 would need 8 (1 for the period). So I thought "Oh ok i need to count the amount of digits it has, no problem!" Boy was I in for a shock.

I made a method to count the digits, counting the left hand side of the decimal place was easy, just first of all cast it as a int to remove the decimal points and divide by 10 (actually I think I had some bitshifting there) and count the amount of times i can do that until i reach 0. And now to count the digits on the right hand side, well i'll just multiply by 10, subtract the digit, and do this until it reaches zero. The method would usually return 32 i think it was. So i WTF'd and had a look at it in debug, turns out when you multiply the float effectively moving the digit columns up because of the well known precision issue it just appended another digit!

I did some major googling, but couldn't really find anything above char str[128] and scanf if in then do strlen(str) minus 1 (null terminator). But i was hoping for a more elegant solution. In the end i just casted it as an int and allowed enough for 9999 fps, also added a check to see if the fps > 9999 but I don't think thats ever going to happen. Better safe than SEG FAULT :(

TLDR: Is there a way to get the amount of digits in a float? How does scanf do it?!

Sorry for long post, just wanted to share my frustation >:D

Edit: spelling errors


Considering it's C++ we're talking about, why not go the STL way?

Precision 5 places after decimal dot, may be variable amount of characters:

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

Precision maximum 5 significant digits:

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


You can truncate/force the floating-point number to whatever precision you want using sprintf. Then the problem goes away.


Since floating point numbers aren't stored in any precise manner, there's not really any effective way to count the number of digits. What you probably want to do is control the length of the number the float provides and use a buffer with a fixed size.

With printf(), scanf() and related functions, the size of a floating point number can be specified by modifying the format type specifier. While a simple %f may be the most common, it can be used more flexibly by adding modifiers between the % and the f, such as:

%[width][.precision]f

where [width] refers to the minimum number of digits to display for the number (there will be no truncation if it goes over), and [.precision] specifies the exact number of digits to display after the decimal point.

By way of example, you can examine the output of the following program:

#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]
}

The exact size of the character array would still be variable depending on the value before the decimal. But so long as you can control the width and the precision, allocating enough memory into a character array (or fitting the number into a fixed-length array) should be substantially simpler.


Well John at CashCommons gave the traditional answer, just use printf format qualifiers to force a certain width. I usually find that 2 decimal places is plenty for frame rates. It allows you to distinguish between 30 fps and 29.97 fps, the two most common values.

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

But if you want to know what the WORST case would be, theres a way to know that as well. Checkout http://en.wikipedia.org/wiki/IEEE_754-2008.

It shows that floating point values (32 bit floats) have 24 binary digits in the mantissa, that works out to 7.225 decimal digits, call it 8. Add 5 digits for the exponent, 1 for the sign, 1 for a leading 0, 1 for the decimal point and you get

 1 + 1 + 8 + 5 = 15 characters

add room for a terminating null and you get 16 digits.


It's frames per second. Just use three digits on the left and one on the right, who cares what the floating point representation happens to think exists below the tenths place?

EDIT in response to the first comment.

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

If we're really after the number of digits in the float, we have to know how floats work. The exact value represented in the IEEE standard float datatype has a certain amount of bits usually 32 or 64, which are allocated in a standardized way to give you a set amount of significant digits, and some power exponent to scale it up or down. 24 bits of significant digits comes out to about seven places in decimals. There's always going to be some form of truncation or rounding error on the end of course (like how one third, written in decimal, always rounds down when you stop writing the repeating threes).

Maybe your number stops after seven or eight digits, but the machine rounding error keeps it going by accident? It just doesnt make sense to read that sort of data.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜