|
inline int RoundValue(float param)
{
int a;
int *int_pointer = &a;
__asm fld param
__asm mov edx,int_pointer
__asm FRNDINT
__asm fistp dword ptr [edx];
return a;
}
int nVal = RoundValue(123.456f);
float fVal = 123.456f;
int nVal = RoundValue(fVal);
inline void FloatToInt(int *int_pointer, const float &f)
{
__asm fld f
__asm mov edx,int_pointer
__asm FRNDINT
__asm fistp dword ptr [edx];
}
int nVal;
float fVal = 123.456f;
FloatToInt(&nVal, fVal);
Regards,
Simon Hughes
E-mail: simon@hicrest.net
Web: www.hicrest.net
|
|
|
|
|
Simon Hughes wrote: __forceinline void FloatToInt(int *int_pointer, float f)
{
__asm fld f
__asm mov edx,int_pointer
__asm FRNDINT
__asm fistp dword ptr [edx];
}
Would it be safer to explicitly set the control word before the round and restoring the previous control word once we're done? Or is this redundant?
It seems that if I wanted to "Round to nearest" (or "Round to even" since that is apparently the intel implementation based on the results for half-integers) I should not assume the control word had not been modified by other code run previously.
It would seem the code is still vulnerable to rounding errors if some prior code had set the control word to something other than the default 037F - Same as FINIT (round to nearest, all exceptions masked, 64-bit precision). This statement is based on the content in MSDN KnowledgeBase article Q126455 - specifically the line that says "Application programmers can avoid rounding errors in the second bug by not overriding the default rounding modes."
Since we are not sure if some prior code overrode the default, we need to explicitly set it to be sure.
By the way, thanks for the article. It has been a big help.
|
|
|
|
|
Does anyone know how to round to an arbitrary number?
For example, rounding to the nearest 2.5, 0.5 or 1.33
Nearest 2.5 case: 2.2 rounds down to 2, 2.3 rounds up to 2.5, 2.7 rounds down to 2.5, 2.8 rounds up to 3
Thanks.
|
|
|
|
|
Did anybody experienced problem with modf function? In most cases it works fine. But in some instances it would not work properly. Ex: 4570.0000 it would return 0.9999999 in fraction and 4569.0000 in integer part. Any ideas?
Thanks
Gene
|
|
|
|
|
The following function is a sample of how to round to the nearest .33 or .66
double GetNearest33( const double dRawNumber )
{
double dBaseVal = floor(dRawNumber);
double dBaseDiff = dRawNumber - dBaseVal;
double dToNearest33 = (double)((int)((dBaseDiff + 0.165)*100.0)/33*33)/100.0;
(dToNearest33 == 0.99)?dToNearest33 = 1.0:dToNearest33;
(dToNearest33 == 0.66)?dToNearest33 = 0.67:dToNearest33;
return dBaseVal + dToNearest33;
}
|
|
|
|
|
this rounds to the nearest x
the idea is to convert the number to fixed point, using the base that you want to round to, then convert back to floating point.
float GetNearest( float number, float fixedBase ) {
if (fixedBase != 0 && number != 0) {
float sign = number>0?1:-1;
number*=sign;
number/=fixedBase;
int fixedPoint = (int)floor(number+.5f);
number = fixedPoint*fixedBase;
number*=sign;
}
return number;
}
|
|
|
|
|
Lately, I've been trying to come up with a good rounding function that will even handle double SNAFUs. If you want a good example of one, simply set a double var to 105, multiply it by 1.15, and then by 20. Like below:
//dddDDDdddDDDdddDDD
double dTmp = 105.0;
dTmp *= 1.15; // equals 120.75....or does it?!!!
dTmp *= 2; // equals 241.5....or does it?!!!
//dddDDDdddDDDdddDDD
Now, when you try to round dTmp with a rounding routine, it will roung down to 241, instead of correctly to 242. I'm sure a lot of you know that this is caused by a limitation of representing fractions in a binary format *1.15 is the cause, I'm pretty sure that 0.15 cannot be represented correctly*.
My question is this: Has anyone seen a rounding alg that can handle one of these "off by .000000000000000001" doubles? I'm currently working on one right now, but my head is starting to get sore from banging it against the wall. I'm experimenting with binary arithmatic on this, but I'm not really sure if that is the way to go. Also, I think that IEEE 754-1984 may hold the answers, but I don't really want to spend $54 on it...
Dere
|
|
|
|
|
In windows I don't know of anyway with the current compilers, used to be under unix you had quad precision as an option. The only thing bad about that was that all you numbers were quad, but none of the intrinsic functions (sin, cos, sqrt) were quad so you had to write your own
|
|
|
|
|
Actually when it comes to rounding it is common practice as well as maybe an IEEE specifiaction that for 0.5 all odd numbers round down and all even number round up. This is so that there is not a statistical anomaly when rounding.
eg. 1.5 rounds to 1, 2.5 rounds to 3.
eg.
(1.5 + 2.5) / 2 = 2
rounded properly
(1 + 3) / 2 = 2
always .5 round up
(2 + 3) / 2 = 2.5
Of course depending on the number of odd and even numbers you get closer to the real average, but you definately don't get further away!
|
|
|
|
|
RoundValue and FloatToInt will not work properly, because frndint instruction will not round to nearest, but to even for equidistant cases (ex. -0.5, 2.5, etc.).
|
|
|
|
|
We all know that floating point numbers aren't necessarily equal. Given the following code:
double x = 1.0;
double y = 1.0;
we cannot assume that this line will return TRUE:
if (x == y)...
I use this function for comparing raw doubles:
//--------------------------------------------------------------------------/
BOOL AlmostEqual(double n1, double n2, int decplaces)
{
if (decplaces == 0)
{
return (floor(n1) == floor(n2));
}
double divider = 10.0;
for (int i = 1; i <= decplaces; i++);
{
divider *= 10;
}
return (n1 >= n2 && n1 < n2 + (1 / divider));
}
Simply pass in the two values you're comparing, and how many decimal places to compare, and you're all set.
|
|
|
|
|
I do something similar, but only take the difference between x and y and compare that to some minimal value (below which I consider to be zero).
eg:
double nThreshold = 0.000001;
// Where nThreshold is smallest number I consider valid
bool IsEqual(double x, double y, double nThreshold)
{
double nDifference = abs(x - y);
return ( nDifference < nThreshold ) ? true : false;
|
|
|
|
|
I agree, FloatsEqual will definitely not work appropriate.
I have this solution, exactly based on the definition of DBL_EPSILON. As I understand this definition DBL_EPSILON is valid only for a window around 1.0.
/*static*/ bool Double::IsEqual(double a, double b, double epsilon /* = DBL_EPSILON */)
{
const double& e = epsilon;
if (b == 0.0) {
// b has all bits zero
if (a > 0.0 || a < 0.0)
// a has significant bits, thus a and b are not equal
return false;
// a has no bit set, thus a and b are equal
return true;
}
const double q = a/b;
if (q<0) {
//q negative, thus a and b have different signs, thus a and b are not equal
return false;
}
//q is positive, thus a and b must have the same sign
//q must be 1.0 if a and b are equal
//check, whether q is in epsilon window around 1.0
if (q > 1.+e) return false;
if (q < 1.-e) return false;
return true;
}
Thomas Haase
|
|
|
|
|
//-----------------------------------------------------------------------------/
double TruncAt(double X, int Offset, double Prec)
{
return (floor(X * pow(10,Offset) + Prec) * pow(10, -Offset));
}
//-----------------------------------------------------------------------------/
double rounder(double n, int places)
{
if (places > 0)
{
double midpoint = 0.0,
placeval = 1.0;
for (int i = 1; i <= places; i++)
placeval *= 0.10;
midpoint = placeval * 5.0; // the value to check for rounding direction
return TruncAt(n, places, midpoint);
}
double fmodresult;
fmodresult = n - floor(n);
if (fmodresult >= 0.500000)
return (n + (1.0 - fmodresult));
else
return (n - fmodresult);
}
|
|
|
|
|
inline double round(double in){
return floor(0.5 + in);
}
|
|
|
|
|
CString FloatToText(double dVal, int nDecPlaces)
{
CString sNum;
sNum.Format("%.*lf", nDecPlaces, dVal);
return sNum;
}
|
|
|
|
|
Well, that does the float to string convert, but doesn't do what I'd use this particular function for, namely, stripping trailing 0's.
I'd add a while loop after the sNum.Format to start at the end of the string, and while the current char is a 0, replace it with a null until you hit the decimal, or the beginning of the string.
|
|
|
|
|
Have you actually tried it?
The asterisk in the format string tells the Format funciton that there's an identifier in the string which defines the number of decimal places to include.
Hence, calling FloatToString(123.450000, 2) would result in "123.45" How is that any different than what you're trying to do?
|
|
|
|
|
I was thinking about your statement after posting my reply, and I thought I'd add this:
If you want a function to strip trailing 0's from a double, you should write a function called "StripTrailingZeros". This function has (according to the prototype) a single purpose, converting a double to a string with a specified precision. It is considered by many to be poor programming technique to make a funciton do something that is not apparent by its prototype or parameter list.
My version of this function does *exactly* what the prototype defines, maintains the positive/negative status of the double, as well as ommitting the '.' from the string if the programmer specified 0 decimal places.
Here's a StripTrailingZeros function for you:
CString StripTrailingZeros(double dValue, int nPrecision)
{
// first we convert it to a string
CString sValue = FloatToString(dValue, nPrecision);
// then we reverse it to make finding 0's easier
sValue.MakeReverse();
// find first NON zero character
int nPos = sValue.FindOneOf(".,123456789");
// if we found one of those chars somewhere other than the 1st char of string
if (nPos > 0)
{
// strip zeros out
sValue.Mid(nPos);
}
// if 1st char is now a dot or comma
if (sValue.GetAt(0) == '.' || sValue.GetAt(0) == ',')
{
// strip it out
sValue.Mid(1);
}
// reverse the string again
sValue.MakeReverse();
// return the massaged string
return sValue;
}
I haven't tested this function, but it should work fine. Just thought I'd throw that out there...
|
|
|
|
|
Actually, I do have something separate I use when stripping zeros, I just created a macro that will strip zero's from the string after conversion. That way, I can do it inline (it's a pretty short piece of code), and not pay for all sorts of function calls and such.
I'm using the stripping on a bunch of values that just get dumped to a listview-like display. Most of the time, the values will have a bunch of trailing zero's that makes the display look busier than I'd like. Also, I'm just using it for columns that I expect the values to be rather integer-ish
|
|
|
|
|
Just to be pedantic, you are not really doing what your function says either, it should real be.
FloatToStringAndStripTrailingZeros, which is better done like so
StripTrailZeros ( FloatToString ( SomeValue, SomePrecision ) )
Then you will have two functions, which do exactly what they say and now more.
But thats me just being pedantic...
|
|
|
|
|
Actually, that would be an overloaded version of StripTrailingZeros, as in...
CString StripTrailingZeroes(CString sVal)
I assumed that most folks here would be able to make this logical association without having to specifically list it as an extention of the function.
:-
|
|
|
|
|
I agree that a separate "remove trailing zeros" fuction is the proper procedure. Trailing zeros HAVE meaning - at least to scientists. Rounding implies significant. 7.00 means a value in the range 6.995 to 7.005 while 7.0 means a value in the range 6.95 to 7.05
|
|
|
|
|
The following global function for rounding without problems
is two years old.
It could be a prototype for the C-Library of VC++.
double __cdecl round( double doValue, int nPrecision )
{
const double doBase = 10.0;
double doComplete5, doComplete5i;
doComplete5 = doValue * pow(doBase, (double) (nPrecision + 1));
doComplete5 += 5.0;
doComplete5 /= doBase;
modf(doComplete5, &doComplete5i);
return doComplete5i / pow(doBase, (double) nPrecision);
}
|
|
|
|
|
I have done some testing with this function and found it to give incorrect results. Try the following:-
float a1 = round(2.15f, 1); // equals 2.2
float a2 = round(2.149f, 1); // equals 2.1
float a3 = round(2.149f, 2); // equals 2.15
float a4 = round(-2.15f, 1); // equals -2.1 ** Incorrect! should be -2.2
float a5 = round(-2.149f, 1); // equals -2.0 ** Incorrect! should be -2.1
float a6 = round(-2.149f, 2); // equals -2.14 ** Incorrect! should be -2.15
float a7 = round(-2.149f, 3); // equals -2.148 ** Incorrect! should be -2.149
float a8 = round(-2.141f, 1); // equals -2.0 ** Incorrect! should be -2.1
float a9 = round(-2.141f, 2); // equals -2.13 ** Incorrect! should be -2.14
float a10= round(-2.141f, 3); // equals -2.14 ** Incorrect! should be -2.141
|
|
|
|
|