How to manipulate the decimal part of a float in C++? - c++

Background:
I have an array of integer times given as 0830 for 08:30, 1745 for 17:45, etc.
I need to calculate the time difference between times. So I'm first turning integer times into floating point times by dividing by 100.0. This means that 0830 will become 8.30.
int inTime = 0830, outTime = 1745;
float newIntime = inTime/100.0;
float newOutTime = outTime/100.0;
The next step, and this is my question, is: How do I divide the decimal part by 0.6 so I get 8.50. This is the only way I'll be able to subtract/add times and get the correct time difference in a meaningful format.
I have not been able to figure out (or look up) a way to multiply the decimal part, i.e. to "access" only what's on the right side of the decimal point. Any help will be appreciated!

float intPart = floor(newInTime);
float converted = intPart + (newInTime - intPart) / 0.6;
And it's better to not use float for this purpose. See others' answer.

Don't convert to float in the first place. Use modulus to extract the minutes:
int inTime = 830;
int inHours = inTime / 100;
int inMinutes = inTime % 100;
int inTotalMinutes = inHours * 60 + inMinutes;
Also, 0830 is not a valid literal. The leading zero causes it to be interpreted as an octal number, and 8 is not a valid octal digit.

You do not want to do this in float, because you will be potentially losing precision.
I would suggest doing something along the line of:
// or use std::tm per Potatoswatter's comment
struct Time
{
int hours;
int minutes;
Time(int time)
{
hours = time/100;
minutes = time-hours*100;
}
};
Time operator-(const Time& a, const Time& b)
{
// You could also just return these minutes and be done with it
int minutes (a.hours-b.hours)*60 + (a.minutes-b.minutes);
Time result;
result.hours = minutes/60;
result.minutes = minutes-result.hours;
}
int main()
{
Time inTime(830), outTime(1745);
Time result = inTime-outTime;
// ...
return 0;
}
Another thing to keep in mind, is that you should not write:
Time inTime(0830);
because the compiler will think that you are talking about octal base (and it cannot have the 8 as a digit).

It is easier and more reasonable to convert 08:30 to 8.5 rather than 8.3 for floating point calculation. It is 8.5 hours from 0:00. Convert back is also easy. Hour is whatever to the left of decimal point and minute is whatever to the right of decimal point multiple by 60.

Extract it with modf. That should eliminate the extra loss of precision.

Related

How to round a floating point type to two decimals or more in C++? [duplicate]

How can I round a float value (such as 37.777779) to two decimal places (37.78) in C?
If you just want to round the number for output purposes, then the "%.2f" format string is indeed the correct answer. However, if you actually want to round the floating point value for further computation, something like the following works:
#include <math.h>
float val = 37.777779;
float rounded_down = floorf(val * 100) / 100; /* Result: 37.77 */
float nearest = roundf(val * 100) / 100; /* Result: 37.78 */
float rounded_up = ceilf(val * 100) / 100; /* Result: 37.78 */
Notice that there are three different rounding rules you might want to choose: round down (ie, truncate after two decimal places), rounded to nearest, and round up. Usually, you want round to nearest.
As several others have pointed out, due to the quirks of floating point representation, these rounded values may not be exactly the "obvious" decimal values, but they will be very very close.
For much (much!) more information on rounding, and especially on tie-breaking rules for rounding to nearest, see the Wikipedia article on Rounding.
Using %.2f in printf. It only print 2 decimal points.
Example:
printf("%.2f", 37.777779);
Output:
37.77
Assuming you're talking about round the value for printing, then Andrew Coleson and AraK's answer are correct:
printf("%.2f", 37.777779);
But note that if you're aiming to round the number to exactly 37.78 for internal use (eg to compare against another value), then this isn't a good idea, due to the way floating point numbers work: you usually don't want to do equality comparisons for floating point, instead use a target value +/- a sigma value. Or encode the number as a string with a known precision, and compare that.
See the link in Greg Hewgill's answer to a related question, which also covers why you shouldn't use floating point for financial calculations.
How about this:
float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);
printf("%.2f", 37.777779);
If you want to write to C-string:
char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);
Always use the printf family of functions for this. Even if you want to get the value as a float, you're best off using snprintf to get the rounded value as a string and then parsing it back with atof:
#include <math.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
double dround(double val, int dp) {
int charsNeeded = 1 + snprintf(NULL, 0, "%.*f", dp, val);
char *buffer = malloc(charsNeeded);
snprintf(buffer, charsNeeded, "%.*f", dp, val);
double result = atof(buffer);
free(buffer);
return result;
}
I say this because the approach shown by the currently top-voted answer and several others here -
multiplying by 100, rounding to the nearest integer, and then dividing by 100 again - is flawed in two ways:
For some values, it will round in the wrong direction because the multiplication by 100 changes the decimal digit determining the rounding direction from a 4 to a 5 or vice versa, due to the imprecision of floating point numbers
For some values, multiplying and then dividing by 100 doesn't round-trip, meaning that even if no rounding takes place the end result will be wrong
To illustrate the first kind of error - the rounding direction sometimes being wrong - try running this program:
int main(void) {
// This number is EXACTLY representable as a double
double x = 0.01499999999999999944488848768742172978818416595458984375;
printf("x: %.50f\n", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.50f\n", res1);
printf("Rounded with round, then divided: %.50f\n", res2);
}
You'll see this output:
x: 0.01499999999999999944488848768742172978818416595459
Rounded with snprintf: 0.01000000000000000020816681711721685132943093776703
Rounded with round, then divided: 0.02000000000000000041633363423443370265886187553406
Note that the value we started with was less than 0.015, and so the mathematically correct answer when rounding it to 2 decimal places is 0.01. Of course, 0.01 is not exactly representable as a double, but we expect our result to be the double nearest to 0.01. Using snprintf gives us that result, but using round(100 * x) / 100 gives us 0.02, which is wrong. Why? Because 100 * x gives us exactly 1.5 as the result. Multiplying by 100 thus changes the correct direction to round in.
To illustrate the second kind of error - the result sometimes being wrong due to * 100 and / 100 not truly being inverses of each other - we can do a similar exercise with a very big number:
int main(void) {
double x = 8631192423766613.0;
printf("x: %.1f\n", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.1f\n", res1);
printf("Rounded with round, then divided: %.1f\n", res2);
}
Our number now doesn't even have a fractional part; it's an integer value, just stored with type double. So the result after rounding it should be the same number we started with, right?
If you run the program above, you'll see:
x: 8631192423766613.0
Rounded with snprintf: 8631192423766613.0
Rounded with round, then divided: 8631192423766612.0
Oops. Our snprintf method returns the right result again, but the multiply-then-round-then-divide approach fails. That's because the mathematically correct value of 8631192423766613.0 * 100, 863119242376661300.0, is not exactly representable as a double; the closest value is 863119242376661248.0. When you divide that back by 100, you get 8631192423766612.0 - a different number to the one you started with.
Hopefully that's a sufficient demonstration that using roundf for rounding to a number of decimal places is broken, and that you should use snprintf instead. If that feels like a horrible hack to you, perhaps you'll be reassured by the knowledge that it's basically what CPython does.
Also, if you're using C++, you can just create a function like this:
string prd(const double x, const int decDigits) {
stringstream ss;
ss << fixed;
ss.precision(decDigits); // set # places after decimal
ss << x;
return ss.str();
}
You can then output any double myDouble with n places after the decimal point with code such as this:
std::cout << prd(myDouble,n);
There isn't a way to round a float to another float because the rounded float may not be representable (a limitation of floating-point numbers). For instance, say you round 37.777779 to 37.78, but the nearest representable number is 37.781.
However, you can "round" a float by using a format string function.
You can still use:
float ceilf(float x); // don't forget #include <math.h> and link with -lm.
example:
float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;
In C++ (or in C with C-style casts), you could create the function:
/* Function to control # of decimal places to be output for x */
double showDecimals(const double& x, const int& numDecimals) {
int y=x;
double z=x-y;
double m=pow(10,numDecimals);
double q=z*m;
double r=round(q);
return static_cast<double>(y)+(1.0/m)*r;
}
Then std::cout << showDecimals(37.777779,2); would produce: 37.78.
Obviously you don't really need to create all 5 variables in that function, but I leave them there so you can see the logic. There are probably simpler solutions, but this works well for me--especially since it allows me to adjust the number of digits after the decimal place as I need.
Use float roundf(float x).
"The round functions round their argument to the nearest integer value in floating-point format, rounding halfway cases away from zero, regardless of the current rounding direction." C11dr ยง7.12.9.5
#include <math.h>
float y = roundf(x * 100.0f) / 100.0f;
Depending on your float implementation, numbers that may appear to be half-way are not. as floating-point is typically base-2 oriented. Further, precisely rounding to the nearest 0.01 on all "half-way" cases is most challenging.
void r100(const char *s) {
float x, y;
sscanf(s, "%f", &x);
y = round(x*100.0)/100.0;
printf("%6s %.12e %.12e\n", s, x, y);
}
int main(void) {
r100("1.115");
r100("1.125");
r100("1.135");
return 0;
}
1.115 1.115000009537e+00 1.120000004768e+00
1.125 1.125000000000e+00 1.129999995232e+00
1.135 1.134999990463e+00 1.139999985695e+00
Although "1.115" is "half-way" between 1.11 and 1.12, when converted to float, the value is 1.115000009537... and is no longer "half-way", but closer to 1.12 and rounds to the closest float of 1.120000004768...
"1.125" is "half-way" between 1.12 and 1.13, when converted to float, the value is exactly 1.125 and is "half-way". It rounds toward 1.13 due to ties to even rule and rounds to the closest float of 1.129999995232...
Although "1.135" is "half-way" between 1.13 and 1.14, when converted to float, the value is 1.134999990463... and is no longer "half-way", but closer to 1.13 and rounds to the closest float of 1.129999995232...
If code used
y = roundf(x*100.0f)/100.0f;
Although "1.135" is "half-way" between 1.13 and 1.14, when converted to float, the value is 1.134999990463... and is no longer "half-way", but closer to 1.13 but incorrectly rounds to float of 1.139999985695... due to the more limited precision of float vs. double. This incorrect value may be viewed as correct, depending on coding goals.
Code definition :
#define roundz(x,d) ((floor(((x)*pow(10,d))+.5))/pow(10,d))
Results :
a = 8.000000
sqrt(a) = r = 2.828427
roundz(r,2) = 2.830000
roundz(r,3) = 2.828000
roundz(r,5) = 2.828430
double f_round(double dval, int n)
{
char l_fmtp[32], l_buf[64];
char *p_str;
sprintf (l_fmtp, "%%.%df", n);
if (dval>=0)
sprintf (l_buf, l_fmtp, dval);
else
sprintf (l_buf, l_fmtp, dval);
return ((double)strtod(l_buf, &p_str));
}
Here n is the number of decimals
example:
double d = 100.23456;
printf("%f", f_round(d, 4));// result: 100.2346
printf("%f", f_round(d, 2));// result: 100.23
I made this macro for rounding float numbers.
Add it in your header / being of file
#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))
Here is an example:
float x = ROUNDF(3.141592, 100)
x equals 3.14 :)
Let me first attempt to justify my reason for adding yet another answer to this question. In an ideal world, rounding is not really a big deal. However, in real systems, you may need to contend with several issues that can result in rounding that may not be what you expect. For example, you may be performing financial calculations where final results are rounded and displayed to users as 2 decimal places; these same values are stored with fixed precision in a database that may include more than 2 decimal places (for various reasons; there is no optimal number of places to keep...depends on specific situations each system must support, e.g. tiny items whose prices are fractions of a penny per unit); and, floating point computations performed on values where the results are plus/minus epsilon. I have been confronting these issues and evolving my own strategy over the years. I won't claim that I have faced every scenario or have the best answer, but below is an example of my approach so far that overcomes these issues:
Suppose 6 decimal places is regarded as sufficient precision for calculations on floats/doubles (an arbitrary decision for the specific application), using the following rounding function/method:
double Round(double x, int p)
{
if (x != 0.0) {
return ((floor((fabs(x)*pow(double(10.0),p))+0.5))/pow(double(10.0),p))*(x/fabs(x));
} else {
return 0.0;
}
}
Rounding to 2 decimal places for presentation of a result can be performed as:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,8),6),2));
For val = 6.825, result is 6.83 as expected.
For val = 6.824999, result is 6.82. Here the assumption is that the calculation resulted in exactly 6.824999 and the 7th decimal place is zero.
For val = 6.8249999, result is 6.83. The 7th decimal place being 9 in this case causes the Round(val,6) function to give the expected result. For this case, there could be any number of trailing 9s.
For val = 6.824999499999, result is 6.83. Rounding to the 8th decimal place as a first step, i.e. Round(val,8), takes care of the one nasty case whereby a calculated floating point result calculates to 6.8249995, but is internally represented as 6.824999499999....
Finally, the example from the question...val = 37.777779 results in 37.78.
This approach could be further generalized as:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,N+2),N),2));
where N is precision to be maintained for all intermediate calculations on floats/doubles. This works on negative values as well. I do not know if this approach is mathematically correct for all possibilities.
...or you can do it the old-fashioned way without any libraries:
float a = 37.777779;
int b = a; // b = 37
float c = a - b; // c = 0.777779
c *= 100; // c = 77.777863
int d = c; // d = 77;
a = b + d / (float)100; // a = 37.770000;
That of course if you want to remove the extra information from the number.
this function takes the number and precision and returns the rounded off number
float roundoff(float num,int precision)
{
int temp=(int )(num*pow(10,precision));
int num1=num*pow(10,precision+1);
temp*=10;
temp+=5;
if(num1>=temp)
num1+=10;
num1/=10;
num1*=10;
num=num1/pow(10,precision+1);
return num;
}
it converts the floating point number into int by left shifting the point and checking for the greater than five condition.

How to correctly store the result of arithmetic operations on int type to double type and different types in C++?

I have two times stored in int type in a struct and want to calculate the no. of hours elapsed in between two times. How do I correctly store the result of in a double variable. I seem to get the difference wrong. Also how do I store the result up to two places after the decimal point.
This is my code :
struct time
{
int hour=0,min=0;
char am_pm='a';
};
int main()
{
time t1,t2;
// GET THE TIME INPUT FROM THE USER HERE
//assuming always t2's hour and min are always numerically greater than t1's hour and min and always `am`
double hour_diff=0.00,min_diff=0.00;
double time_elapsed=0.00;
cout<<"The time elapsed between your entered times is : ";
hour_diff=t2.hour-t1.hour; counting hour difference
min_diff=(t2.min+t1.min)/60; //counting total minutes and converting them into hours
time_elapsed=hour_diff+min_diff;
cout<<time_elapsed;
if i give these input i get wrong result 7 when i should get 7.25 :
INPUT
t1.hour = 5
t1.min = 30
t1.am_pm = a;
t2.hour = 11
t2.min = 45
t2.am_pm = a;
time elapsed = 7 // this is wrong, I should be getting 7.25
The error is because this expression (t2.min+t1.min)/60 will return int.
That's because (t2.min+t1.min) is of type int and 60 is of type int. Hence \ will be an integer division operation.
To resolve it you can convert your (t2.min+t1.min) to the double with static_cast<double>(t2.min+t1.min). See more about static_cast.
Or you can simply define 60 as a double by writing 60.0.
Since you are performing integer operations '(t2.min + t1.min)/60', even though you are storing them in a variable of type double, become simplified to an integer type.
Either make 60 a double by changing it to '60.0' or encompass the whole result with a 'static_cast' before your operations.

How can I find the amount of numbers in double?

Let's say I have an input 1.251564.
How can I find how many elements are after "." to have an output as follows:
int numFloating;
// code to go here that leads to
// numFloating == 6
p.s. Sorry for not providing any code, I just have no idea how that should be implemented :(
Thanks for your answers!
Let us consider your number, 1.251564. When you store this in a double, it is stored in the binary IEEE754 format. And you might find that the number is not representable. So, let us check for this number. The closest representable double is:
1.25156 39999 99999 89880 45035 73046 53152 82344 81811 52343 75
This probably comes as something of a surprise to you. There are 52 decimal digits following the decimal point.
The lesson that you need to take away from this is that if you want to ask questions about decimal representations, you need to use a decimal data type rather than double. Once you can actually represent the value exactly, then you will be able to reason about it in a manner that matches your expectations.
Simplest way would be to store it in string.
std::string str("1.1234");
size_t length = str.length();
size_t found = str.find('.', 0 );
size_t count = length-found-1;
int finallyGotTheCount = static_cast<int>(count);
This won't end up well. The problem is that sometimes there are float errors when representing numbers in binary (which is what your computer does).
For example, when adding 1 / 3 + 1 / 3 + 1 / 3 you might get 0.999999... and the number of decimal places varies greatly.
ravi already provided a good way to calculate it, so I'll provide a different one:
double number = 0; // should be equal to the number you want to check
int numFloating = 0;
while ((double)(int)number != number){
number *= 10;
numFloating++;
}
number is a double variable that holds the number you want to check for decimal places.
If you have a fractional number. Lets say .1234
Repeatedly multiply by 10 and throw away the integer portion of the number until you get zero. The number of steps will be the number of decimals. e.g:
.1234 * 10 = 1.234
.234 * 10 = 2.34
.34 * 10 = 3.4
.4 * 10 = 4.0
Problems will however occur when you have a number that is "floating" like 1.199999999.
int numFloating = 0;
double orgin = 1.251564;
double value = orgin - floor(orgin);
while(value == 0)
{
value *= 10;
value = value - floor(value);
numFloating ++;
}
By using this code sometimes answer is wrong. exp: zero in floating point is equal to (2^31)-1.
Obviously output depends on how it realy stored.

Convert time input to a float to enable math functions in C++

I am asking the user to input a start time and an end time in this format: 15.45 (military time with a decimal instead of a colon) and I need to convert those times to a float to perform calculations on them. I am having trouble wrapping my mind around converting the 60 minutes of an hour to a decimal value. e.g. - User inputs start of 12.45 and end of 14.15, this should be converted to 12.75 and 14.25 respectively. How would I go about this conversion?
Also, something I believe I am more capable of figuring out, but curious anyway: how would I validate input so as not to allow a time greater than 23.59 and no time with the last two digits greater than 59?
As soon as one of the values is double, then the arithmetic will be
done in double, with double results. So you can convert the
minutes to double, using any of the three C++ syntaxes:
static_cast<double>(minutes), double(minutes) or (double)minutes,
or just divide by 60.0, rather than the integer 60. (I like to be
explicit, so I'd write double(minutes) / 60.0. And some people prefer
static_cast, even in this case.)
With regards to the validation, I'd do it before conversion; once you've
added the minutes to the hours, it's too late anyway.
just use
double time = hours + minutes / 60.0;
bool hoursValid = hours < 24; // hours are unsigned, right?
bool minutesValid = minutes < 60;
Example:
char buf[] = "9.45";
unsigned int hours, minutes;
sscanf(buf, "%d.%d", &hours, &minutes);
printf("%f", hours + minutes/60.0); // outputs 9.75
If you get double 9.45 as input, you need to #include <cmath> and split it as
hours = floor(v);
minutes = (v - hours) * 100;
You have to separate the integer and decimal components of the number. You will keep the integer part intact, then add the decimal part divided by 60 to it.
Validation is also simple once you separate into hours and minutes.
I'm going to assume your code reads the value in from the user as a string. You will need to parse the user input at the decimal point to get the hours and minutes as two separate values. Once you have that, then you can convert minutes into a fraction of an hour and add it to the number of hours.
bool military_time_valid(float a) {
int t = (int)a;
if (t >= 24)
throw std::runtime_error("invalid hours");
if (a-t >= .6)
throw std::runtime_error("invalid minutes");
}
float military_to_decimal(float a) {
int t = (int)a;
return t + (a-t)/.6;
}
float decimal_to_military(float a) {
int t = (int)a;
return t + (a-t)*.6;
}
float t = 15.45;
int h = (int) t;
float m = 100 * (t - h);
printf("%f", h + m / 60);
gives output: 15.750000
Check if h is between 0 and 23 and m is between 0 and 59.
Is there some reason you don't want to create a MilTime class and overload the arithmetic operators? That would save you from having to convert to/from all the time, and would more clearly state your intentions.

C/C++ rounding up decimals with a certain precision, efficiently

I'm trying to optimize the following. The code bellow does this :
If a = 0.775 and I need precision 2 dp then a => 0.78
Basically, if the last digit is 5, it rounds upwards the next digit, otherwise it doesn't.
My problem was that 0.45 doesnt round to 0.5 with 1 decimalpoint, as the value is saved as 0.44999999343.... and setprecision rounds it to 0.4.
Thats why setprecision is forced to be higher setprecision(p+10) and then if it really ends in a 5, add the small amount in order to round up correctly.
Once done, it compares a with string b and returns the result. The problem is, this function is called a few billion times, making the program craw. Any better ideas on how to rewrite / optimize this and what functions in the code are so heavy on the machine?
bool match(double a,string b,int p) { //p = precision no greater than 7dp
double t[] = {0.2, 0.02, 0.002, 0.0002, 0.00002, 0.000002, 0.0000002, 0.00000002};
stringstream buff;
string temp;
buff << setprecision(p+10) << setiosflags(ios_base::fixed) << a; // 10 decimal precision
buff >> temp;
if(temp[temp.size()-10] == '5') a += t[p]; // help to round upwards
ostringstream test;
test << setprecision(p) << setiosflags(ios_base::fixed) << a;
temp = test.str();
if(b.compare(temp) == 0) return true;
return false;
}
I wrote an integer square root subroutine with nothing more than a couple dozen lines of ASM, with no API calls whatsoever - and it still could only do about 50 million SqRoots/second (this was about five years ago ...).
The point I'm making is that if you're going for billions of calls, even today's technology is going to choke.
But if you really want to make an effort to speed it up, remove as many API usages as humanly possible. This may require you to perform API tasks manually, instead of letting the libraries do it for you. Specifically, remove any type of stream operation. Those are slower than dirt in this context. You may really have to improvise there.
The only thing left to do after that is to replace as many lines of C++ as you can with custom ASM - but you'll have to be a perfectionist about it. Make sure you are taking full advantage of every CPU cycle and register - as well as every byte of CPU cache and stack space.
You may consider using integer values instead of floating-points, as these are far more ASM-friendly and much more efficient. You'd have to multiply the number by 10^7 (or 10^p, depending on how you decide to form your logic) to move the decimal all the way over to the right. Then you could safely convert the floating-point into a basic integer.
You'll have to rely on the computer hardware to do the rest.
<--Microsoft Specific-->
I'll also add that C++ identifiers (including static ones, as Donnie DeBoer mentioned) are directly accessible from ASM blocks nested into your C++ code. This makes inline ASM a breeze.
<--End Microsoft Specific-->
Depending on what you want the numbers for, you might want to use fixed point numbers instead of floating point. A quick search turns up this.
I think you can just add 0.005 for precision to hundredths, 0.0005 for thousands, etc. snprintf the result with something like "%1.2f" (hundredths, 1.3f thousandths, etc.) and compare the strings. You should be able to table-ize or parameterize this logic.
You could save some major cycles in your posted code by just making that double t[] static, so that it's not allocating it over and over.
Try this instead:
#include <cmath>
double setprecision(double x, int prec) {
return
ceil( x * pow(10,(double)prec) - .4999999999999)
/ pow(10,(double)prec);
}
It's probably faster. Maybe try inlining it as well, but that might hurt if it doesn't help.
Example of how it works:
2.345* 100 (10 to the 2nd power) = 234.5
234.5 - .4999999999999 = 234.0000000000001
ceil( 234.0000000000001 ) = 235
235 / 100 (10 to the 2nd power) = 2.35
The .4999999999999 was chosen because of the precision for a c++ double on a 32 bit system. If you're on a 64 bit platform you'll probably need more nines. If you increase the nines further on a 32 bit system it overflows and rounds down instead of up, i. e. 234.00000000000001 gets truncated to 234 in a double in (my) 32 bit environment.
Using floating point (an inexact representation) means you've lost some information about the true number. You can't simply "fix" the value stored in the double by adding a fudge value. That might fix certain cases (like .45), but it will break other cases. You'll end up rounding up numbers that should have been rounded down.
Here's a related article:
http://www.theregister.co.uk/2006/08/12/floating_point_approximation/
I'm taking at guess at what you really mean to do. I suspect you're trying to see if a string contains a decimal representation of a double to some precision. Perhaps it's an arithmetic quiz program and you're trying to see if the user's response is "close enough" to the real answer. If that's the case, then it may be simpler to convert the string to a double and see if the absolute value of the difference between the two doubles is within some tolerance.
double string_to_double(const std::string &s)
{
std::stringstream buffer(s);
double d = 0.0;
buffer >> d;
return d;
}
bool match(const std::string &guess, double answer, int precision)
{
const static double thresh[] = { 0.5, 0.05, 0.005, 0.0005, /* etc. */ };
const double g = string_to_double(guess);
const double delta = g - answer;
return -thresh[precision] < delta && delta <= thresh[precision];
}
Another possibility is to round the answer first (while it's still numeric) BEFORE converting it to a string.
bool match2(const std::string &guess, double answer, int precision)
{
const static double thresh[] = {0.5, 0.05, 0.005, 0.0005, /* etc. */ };
const double rounded = answer + thresh[precision];
std::stringstream buffer;
buffer << std::setprecision(precision) << rounded;
return guess == buffer.str();
}
Both of these solutions should be faster than your sample code, but I'm not sure if they do what you really want.
As far as i see you are checking if a rounded on p points is equal b.
Insted of changing a to string, make other way and change string to double
- (just multiplications and addion or only additoins using small table)
- then substract both numbers and check if substraction is in proper range (if p==1 => abs(p-a) < 0.05)
Old time developers trick from the dark ages of Pounds, Shilling and pence in the old country.
The trick was to store the value as a whole number fo half-pennys. (Or whatever your smallest unit is). Then all your subsequent arithmatic is straightforward integer arithimatic and rounding etc will take care of itself.
So in your case you store your data in units of 200ths of whatever you are counting,
do simple integer calculations on these values and divide by 200 into a float varaible whenever you want to display the result.
I beleive Boost does a "BigDecimal" library these days, but, your requirement for run time speed would probably exclude this otherwise excellent solution.