PDA

View Full Version : QStrings and Floats, Current Precision?



tescrin
11th July 2012, 18:49
So I'm down to my last little bug in a keypad I built; getting the precision to not be...exorbitant. I've checked through the docs and haven't found any methods that detect the number of significant digits. I'm getting ready to write a substantial bit of code (at least by compared to the task at hand) and adding potentially needless state to an object to deal with keeping track. I can do this, but I'm just curious beforehand if there's any sort of detect precision functionality in Qt.

Note: I know it will figure it out itself when it converts to scientific notation (or at least it'll figure it out good enough.) My algorithm to deal with it basically entails chopping off the E(blah) part, dealing with the current decimal if needed (moving or deleting it) and appending (or prepending) zeros to my QString so it reads the way I want it to (I.E. so it reads just like the 'f' argument would, but with a precision dependent on the input.) This may not work; as I'm assuming Qt doesn't use scientific notation with some set number of decimals, but uses it like actual scientific notation.

Thanks!

wysota
12th July 2012, 09:20
Qt has nothing to do with this. You are using floating point numbers as defined by the language (C++) and all rules that apply to that language, apply to your Qt program. "Notations" are just what they are called -- ways to enter and display numbers. They have nothing to do with how the number is really kept in memory. As the name suggests, you are dealing with "floating point" numbers (i.e. the precision is not statically defined but rather dependent on the value itself). Maybe what you want is "fixed point" arithmetics. Unfortunately C/C++ don't have such a data type built in so you have to either roll out your own or use a library that provides such a data type.

high_flyer
12th July 2012, 09:47
@OP
Maybe I don't understand correctly but if what you are concerned about is displaying float number with a fixed trailing numbers after the point, have a look at QString::arg() overloads.

tescrin
12th July 2012, 16:16
I thank you, but I think I've been unclear. I understand I'm dealing with floats (or similarly doubles). I'm trying to display a *dynamic* number of significant digits

-Qt defines significant digits as digits after the decimal (just to make sure we're on the same page.)
-Qt QStrings default to showing 5 significant digits if using the 'f' argument
-QStrings (seem) to default to showing 3 significant digits if using the 'e'/'E' arguments

What I'm trying to do is when the user enters a number: 1.1, that when they scale it E5, it will say 110000, not 110000.0 (or .00, .000, .0000, .00000, etc..) and that when they scale it down E2 it will say (0).011. Qt would in these two cases offer me the options (unless I'm missing something) the ability to display it as 110000.00000 or 1.100E+5 (or similar) and .01100 or 1.100E-2 (or similar.)

Again, I get that it's underlying C++, but C++ doesn't have a default functionality and uses "setprecision(X)" (also not helpful.) I'm just double-checking that there's no *dynamic* significant digit feature (I.E. show only the relevant digits.)

I will note that C++ defaults to this, but QStrings interpret the float directly. I.E. in C++ if you have x = 1.1 and y = 3.55 :
cout << x << " " << y; will display
1.1 3.55.

I'm guessing thinking about it that Qt has some local setprecision method that's been set already. Hmm...

EDIT: I bet I could just use floating point and chop off trailing '0's if there's a decimal point.


EDIT: What I did:
(this is called whenever something wants to update the display with a floating point rather than the usual input)


void EnterYourSuperCoolQStringFunctionNameHere(QString input)
{
//remove trailing zeros or a trailing decimal if we have a decimal point
while(input.contains('.')){
//if it ends with a zero or decimal, remove it
if(input.endsWith('0') || input.endsWith('.'))
input.chop(1);
else
break;
}
//input is now acceptable.
this->setText(input);
}

Again, if this is repeated functionality it'd be good to know. Otherwise, w00t. Tested with scalars, appended input, etc.., it all works now.

wysota
12th July 2012, 16:45
QString::arg() has variants allowing you to specify the number of digits displayed. But it doesn't change the fact that 1.1 divided by E5 and then multiplied back by E5 might not give you 1.1 again. It might be 1.1 but it might be 1.100001 or something like that. You can't dynamically determine precision because the number is kept as binary and not decimal. Of course you can chop off zeroes but then you can get bad results. You have to employ rounding for that.

tescrin
12th July 2012, 17:44
This is less about getting exact numbers. This was a replication of something akin to a calculator. The display should only display what the user has entered or a reasonable approximation of it (i.e. if they multiply by 2*2, they don't need 4.00000)

The problem with the Qt args is the same as me chopping off zeros if you want to be technical. Whenever you setprecision you're implicitly rounding. Just because QStrings will do this automatically does not afford you some kind of extra.. er... correctness. Further, setting the precision is not precise! Just because you default to pretending you multiplied by 1.000000 doesn't mean you did. If it were any sort of science class you'd be incorrect to assume such precision.

That said, were it in a program for anything but displaying; I wouldn't be too worried about chopping trailing zeros (at least.. I don't think I would?) But for displaying all of the "user-entered significant digits" without displaying excess, as you'd expect from a calculator or something that functions like one, you are more worried about what's pleasing to the user.

Further, the excess posses problems. Something that functions similar to a calculator appends digits when you press them. This means that if a user wants to press the button and there's a bunch of decimals it either:
-appends to the end of these decimals meaning that the user must first clear the box and then enter their number(s)
-appends it before the decimal, in which case it's unintuitive. After all, all calcs just append to the end!

Hence my solution. It seems reasonable that default C++ functionality or that some removal-of-useless-digits function would exist in the QString, but oddly enough it does not. Given that the *default* in C++ is to just show what's actually there I wanted to ask if Qt would do the same.

Surely the *default* C++ functionality isn't an unreasonable request. :)


EDIT: That brings me to an odd idea: hooking up the output stream (the C++ cout variable) to a QLabel or QTextEdit to display what I want; just as an experiment.

wysota
12th July 2012, 18:06
I don't see how Qt or C++ would have to guess what you as the user consider "useless". My point of view is that if you want a decimal calculator, you use decimal (aka fixed point) math.You can calculate everything using integers and keep the precision as a separate value and then just put the decimal point in the right place when showing the result.

tescrin
12th July 2012, 18:50
I think I can see what you're saying. I did something akin to that elsewhere in the program (or rather a program that uses this keypad dialog.) Still, how many calculators have you used that preserve the actual number of digits? IIRC even my TI-83+ and TI-92 may not have done that (it's hard to remember having not used them for a year or two.)

But I think you still misunderstand. C++ *already* does that. Similar to what I said before if you do:

float x=10.0000, y=3.0010;
cout<<x*y;

the output will be 30.01, not 30.010 (or 30.0100), which true precision would say the result *is*. This is *default functionality* is what I'm saying; and I'm surprised that Qt went out of it's way to "improve" upon functionality without also allowing you to print it the way it would normally.

What I'm calling "useless digits" are the same digits the default truncation C++ gets rid of, however accurate it would be in real world scientific application. Note also, that your calculator would be incorrect if given two values like the above (if I understand you correctly):


float x=10, y=3.0010;
cout<<x*y;
This would output under your code (assuming I understand you) 30.010 (or 30.0100), which is incorrect. We can only know that it is 30. Further more, if a user is using your calculator which value do they expect? They expect that you didn't get rid of their .01 for the sake of precision correctness. They'd expect it to be the "incorrect" or "unprecise" value because they're presumably either smart enough to truncate the amount they were not supposed to use, or don't care to.

I argue that in *any* user realm, the user expects the calculator to give it all of it's information and they don't want several trailing zeros. Again, in acadamia or research you'd likely go figure out the precision yourself and rather have the calculator just calculate.


Disclaimer: I hate to turn this into a topic about the philosophy of keypad precision :S. Technically my question is answered and a solution is posted.


EDIT: It's worth noting that I understand C++ isn't technically truncating digits; that instead it's never saving them in the floating point format because there's no need to. It doesn't change my point at all.

wysota
12th July 2012, 19:19
the output will be 30.01, not 30.010 (or 30.0100),
Sure it will but not because of C++ but rather because the binary representation of all these is the same. Your computer simply doesn't differentiate between them. But if you write a decimal number that doesn't have a binary representation then you won't get what you entered but rather what the actual binary representation closest to what you entered is. That's how floating point works. The precision of the number is not determined by any calls to setprecision() (whatever that is) but by the value itself. Some values (like 0.1) do not have a binary representation. What cout does is (probably) that it rounds the binary value to a human-readable decimal representation. QTextStream does the same thing.


#include <QtCore>
#include <iostream>

int main() {
QString s;
QTextStream str(&s);
str << 0.000001;
qDebug() << s;
std::cout << 0.000001 << std::endl;
return 0;
}

"1e-06"
1e-06

(both will show "0.01" for 0.01)


This all doesn't change the fact that when using floating point, you're risking not showing the actual value that is used for calculation. I would never risk using such calculator for serious calculations, to be honest...

tescrin
12th July 2012, 20:11
Ha! I didn't realize you meant to not use floating point at all. For sure.

As it is, this is a simple keypad for user input (it doesn't even do calculations, sort of...) That said, yeah, I can agree with that. Binary representation has it's practical limits.

EDIT: You've never used "setprecision()"? Its a standard C++ cout uh.. right-hand variable. I.E. setprecision(X) limits all cout functionality to read to X decimal places similar to QString::number(<NUM>,'f',X) regardless of the number.

Also, you keep talking as if I didn't make it clear that I understand how floating point is represented. I even displayed so in an example (and mentioned why.) It's worth noting that Cout isn't really doing anything to the value other than allowing the floating point to be interpreted; just as you said there's no distinction for extra zeros. [You're setting floating point bits and then leaving the exponent. There's no real way to set extra zeros in binary without always setting all of them.]

The main issue in this discussion has been that we're viewing the word "Precision" differently. I'm using as the Qt term (and apparently C++ term) meaning "number of decimal places to use" and you seem to be remarking on the actual precision of the value.

wysota
12th July 2012, 20:35
As it is, this is a simple keypad for user input (it doesn't even do calculations, sort of...)
Then why do you keep it as a numeric value instead of keeping it as text in the first place?


EDIT: You've never used "setprecision()"? Its a standard C++ cout uh.. right-hand variable.
Then it is std::ostream::setprecision() or something like that. And no, I have never used it, at least not that I remember using it. There is QTextStream::setRealNumberPrecision() if you want it.


It's worth noting that Cout isn't really doing anything to the value other than allowing the floating point to be interpreted
It is the one interpreting it so yeah... I'd say it does something to the value.


just as you said there's no distinction for extra zeros.
That's different. That's done by the compiler (or actually by the parser).


[You're setting floating point bits and then leaving the exponent. There's no real way to set extra zeros in binary without always setting all of them.]
To be honest there are no zeroes there at all. There are (usually) two binary values --- mantissa and exponent. And they are both of fixed width that can contain a lot of zeroes at the end.


The main issue in this discussion has been that we're viewing the word "Precision" differently. I'm using as the Qt term (and apparently C++ term) meaning "number of decimal places to use" and you seem to be remarking on the actual precision of the value.

No, we are interpreting this word the same way. I'm just saying you either have fixed precision or not. There is no "magically-determine-precision-for-me" precision. I'm sure there are lots and lots of decimal values that "cout" (meaning std::ostream) will show in a different way than what you input in your source code file (ignoring any trailing zeroes that do not change the actual value) or in some text field. The more fine grained values you use the higher probability of such behaviour. "cout" can guess that when you give it 0.100000001 then you probably mean 0.1 but at some point you might want to show 0.100000001 and it will instead display 0.1. It's all about heuristics and those are by definition not always correct.