I hate floating point operations
-
Very true and that's why I said, "A tactic similar to this can be used." Personally I always compare to a "tolerance" value and that works well. The big issue is - what do you use for a tolerance value ? That varies according to the circumstances as you said.
-
Very true and that's why I said, "A tactic similar to this can be used." Personally I always compare to a "tolerance" value and that works well. The big issue is - what do you use for a tolerance value ? That varies according to the circumstances as you said.
-
Ravi's statement still holds. Floating point addition is bad, multiplication is good. What is 20.0 + 0.000000000000000000000000000001? 20 There isn't enough mantissa to hold all the digits. Then you add in the fact that floating point is basically base 2 while our math is base 10, floating point doesn't have much hope of representing numbers exactly. That is why banks used such things as scaled integers.
Tim Smith I'm going to patent thought. I have yet to see any prior art.
Tim Smith wrote:
Ravi's statement still holds. Floating point addition is bad, multiplication is good.
Mine still holds too, beware atof. I believe you could get the same result without any addition or multiplication (whose I doubt it is good). Introduction of an epsilon by atof is not indicated in the documentation[^]. Some might be fooled.
Tim Smith wrote:
scaled integers
Replacing a double by a structure of an integer and a floating point position?
Where do you expect us to go when the bombs fall?
-
I really do think the compiler should throw an error when you try to compare floating point values for equality.
cheers, Chris Maunder
CodeProject.com : C++ MVP
A warning may be sufficient, like the ';' after a 'if'. In my case, that would not have been enough. Guys who made that code didn't believe in warnings. When I reactivated the compiler option, over 1,400 warnings popped up at the first rebuild. Yeepee.
Where do you expect us to go when the bombs fall?
-
A warning may be sufficient, like the ';' after a 'if'. In my case, that would not have been enough. Guys who made that code didn't believe in warnings. When I reactivated the compiler option, over 1,400 warnings popped up at the first rebuild. Yeepee.
Where do you expect us to go when the bombs fall?
K(arl) wrote:
over 1,400 warnings popped up at the first rebuild
:omg: A cardinal sin. Everything we do here is warning level 3 or higher, with "warning as errors" on release builds.
Kicking squealing Gucci little piggy.
The Rob Blog -
Tim Smith wrote:
Ravi's statement still holds. Floating point addition is bad, multiplication is good.
Mine still holds too, beware atof. I believe you could get the same result without any addition or multiplication (whose I doubt it is good). Introduction of an epsilon by atof is not indicated in the documentation[^]. Some might be fooled.
Tim Smith wrote:
scaled integers
Replacing a double by a structure of an integer and a floating point position?
Where do you expect us to go when the bombs fall?
K(arl) wrote:
Tim Smith wrote: scaled integers Replacing a double by a structure of an integer and a floating point position?
Possible I suppose, but storing the value in cents, not dollars would be a simpler method.
-- Rules of thumb should not be taken for the whole hand.
-
<Using MFC>
double dValue = atof("0.1"); ASSERT(dValue == 0.1); double dSecondValue = (1 + dValue + dValue + dValue + dValue); ASSERT(dSecondValue == 1.4); // Crash
Where do you expect us to go when the bombs fall?
Try this, this is what I use in every of my code :
double dValue = atof("0.1");
double dTest = 0.1;
ASSERT
(
((*((LONGLONG*)&dValue))&0xFFFFFFFFFFFFFF00)
== ((*((LONGLONG*)&dTest)) &0xFFFFFFFFFFFFFF00)
);double dSecondValue = (1 + dValue + dValue + dValue + dValue);
double dTest2 = 1.4;
ASSERT
(
(*((LONGLONG*)&dSecondValue)&0xFFFFFFFFFFFFFF00)
== (*((LONGLONG*)&dTest2) &0xFFFFFFFFFFFFFF00)
); // *NO* CrashBy reducing mantissa's complexity (skiping lasting bits) by an interger cast (mostly like an union over a double), you can do some pretty decent comparison with no headache... By using float (4 bytes) instead, you could simply things to :
float dValue = atof("0.1");
float dTest = 0.1;
ASSERT
(
((*((int*)&dValue))&0xFFFFFFF0)
== ((*((int*)&dTest)) &0xFFFFFFF0)
);float dSecondValue = (1 + dValue + dValue + dValue + dValue);
float dTest2 = 1.4;
ASSERT
(
(*((int*)&dSecondValue)&0xFFFFFFF0)
== (*((int*)&dTest2) &0xFFFFFFF0)
); // *NO* CrashThe problem comes mostly because the preprocessor code which convert
double dTest = 0.1
is *NOT* the same than the code within ATOF which convertdouble dValue = atof("0.1")
. So you don't get a bitwise exact match of the value, only a close approximation. By using the cast technique, you : 1- can control over how many bits how want to perform the comparison 2- do a full integer comparison, which is faster by far than loading floating point registers to do the same 3- etc... So define the following macros :#define DCMP(x,y) ((*((LONGLONG*)&x))&0xFFFFFFFFFFFFFF00)==((*((LONGLONG*)&y))&0xFFFFFFFFFFFFFF00)
#define FCMP(x,y) (*((int*)&x)&0xFFFFFFF0)==(*((int*)&y)&0xFFFFFFF0)Use
DCMP
on double, andFCMP
on float... But beware, you cannot do that :ASSERT(DCMP(atof("0.1"),0.1)); // atof returns a value which have to be stored...
The following code works :
#define FCMP(x,y) (*((int*)&x)&0xFFFFF000)==(*((int*)&y)&0xFFFFF000)
float dSecondValue = atof("1.4"); // RAW : 0x3FB332DF
float dTest2 = 1.39999; // RAW : 0x3FB33333, last 12 bits are differents, so don't compare them
ASSERT(FCMP(dSecondValue,dTest2)); // *NO* CrashKochise EDIT : you may have used a
memcmp
approach, which is similar in functionality, but you can only test on byte boundaries (base of lenght of comparison is byte) and x86 is little endian, so you start comparing the different bytes first, -
Tim Smith wrote:
Ravi's statement still holds. Floating point addition is bad, multiplication is good.
Mine still holds too, beware atof. I believe you could get the same result without any addition or multiplication (whose I doubt it is good). Introduction of an epsilon by atof is not indicated in the documentation[^]. Some might be fooled.
Tim Smith wrote:
scaled integers
Replacing a double by a structure of an integer and a floating point position?
Where do you expect us to go when the bombs fall?
Read any book on the issues of floating point math and it will tell you that floating point addition is inherently more imprecise that floating point multiplication. For example, this is bad. You accumulate small error all the time: float x = 10; for (i = 0; i < 1000; i++) x += 0.05; This is much better but can still have a problem with the addition: float x = 10; for (i = 0; i < 1000; i++) float x1 = x + (i * 0.05);
Tim Smith I'm going to patent thought. I have yet to see any prior art.
-
Try this, this is what I use in every of my code :
double dValue = atof("0.1");
double dTest = 0.1;
ASSERT
(
((*((LONGLONG*)&dValue))&0xFFFFFFFFFFFFFF00)
== ((*((LONGLONG*)&dTest)) &0xFFFFFFFFFFFFFF00)
);double dSecondValue = (1 + dValue + dValue + dValue + dValue);
double dTest2 = 1.4;
ASSERT
(
(*((LONGLONG*)&dSecondValue)&0xFFFFFFFFFFFFFF00)
== (*((LONGLONG*)&dTest2) &0xFFFFFFFFFFFFFF00)
); // *NO* CrashBy reducing mantissa's complexity (skiping lasting bits) by an interger cast (mostly like an union over a double), you can do some pretty decent comparison with no headache... By using float (4 bytes) instead, you could simply things to :
float dValue = atof("0.1");
float dTest = 0.1;
ASSERT
(
((*((int*)&dValue))&0xFFFFFFF0)
== ((*((int*)&dTest)) &0xFFFFFFF0)
);float dSecondValue = (1 + dValue + dValue + dValue + dValue);
float dTest2 = 1.4;
ASSERT
(
(*((int*)&dSecondValue)&0xFFFFFFF0)
== (*((int*)&dTest2) &0xFFFFFFF0)
); // *NO* CrashThe problem comes mostly because the preprocessor code which convert
double dTest = 0.1
is *NOT* the same than the code within ATOF which convertdouble dValue = atof("0.1")
. So you don't get a bitwise exact match of the value, only a close approximation. By using the cast technique, you : 1- can control over how many bits how want to perform the comparison 2- do a full integer comparison, which is faster by far than loading floating point registers to do the same 3- etc... So define the following macros :#define DCMP(x,y) ((*((LONGLONG*)&x))&0xFFFFFFFFFFFFFF00)==((*((LONGLONG*)&y))&0xFFFFFFFFFFFFFF00)
#define FCMP(x,y) (*((int*)&x)&0xFFFFFFF0)==(*((int*)&y)&0xFFFFFFF0)Use
DCMP
on double, andFCMP
on float... But beware, you cannot do that :ASSERT(DCMP(atof("0.1"),0.1)); // atof returns a value which have to be stored...
The following code works :
#define FCMP(x,y) (*((int*)&x)&0xFFFFF000)==(*((int*)&y)&0xFFFFF000)
float dSecondValue = atof("1.4"); // RAW : 0x3FB332DF
float dTest2 = 1.39999; // RAW : 0x3FB33333, last 12 bits are differents, so don't compare them
ASSERT(FCMP(dSecondValue,dTest2)); // *NO* CrashKochise EDIT : you may have used a
memcmp
approach, which is similar in functionality, but you can only test on byte boundaries (base of lenght of comparison is byte) and x86 is little endian, so you start comparing the different bytes first,Your code still doesn't work since it suffers from boundary conditions. For example: 0xFFFFFF00 0xFFFFFEFF These are two very close floating point numbers, but your test will fail. Also, there are problems with -0 and +0. bool CMP (float x, float y, int tol) { int ix = *((int *) &x); int iy = *((int *) &y); if (ix < 0) ix = 0x80000000 - ix; if (iy < 0) iy = 0x80000000 - iy; return abs (ix - iy) <= tol; } This fixes the boundary condition and the +0, -0 issue. However, it still has problems with such things as +inf and -inf being close to +/- MAX_FLT and other issues with special floating point bit patterns.
Tim Smith I'm going to patent thought. I have yet to see any prior art.
-
Your code still doesn't work since it suffers from boundary conditions. For example: 0xFFFFFF00 0xFFFFFEFF These are two very close floating point numbers, but your test will fail. Also, there are problems with -0 and +0. bool CMP (float x, float y, int tol) { int ix = *((int *) &x); int iy = *((int *) &y); if (ix < 0) ix = 0x80000000 - ix; if (iy < 0) iy = 0x80000000 - iy; return abs (ix - iy) <= tol; } This fixes the boundary condition and the +0, -0 issue. However, it still has problems with such things as +inf and -inf being close to +/- MAX_FLT and other issues with special floating point bit patterns.
Tim Smith I'm going to patent thought. I have yet to see any prior art.
My macro can be of great help if you know where you put your foot. Eg when dealing with strict positive numbers set, or strict negative numbers set, without mixing the two. However the test case only works with 0xFF... values padded with 0, not like your 0xFFFFFEFF example. I think you wanted to say 0xFFFFFE00 which is correct :) Kochise PS : If I remember right, there is a 'magical trick' explained in a raticle on CP which explain how to cast double to float and the way back only using integer operations, and it works pretty well and fast, and also deals with the sign...
In Code we trust !
-
Read any book on the issues of floating point math and it will tell you that floating point addition is inherently more imprecise that floating point multiplication. For example, this is bad. You accumulate small error all the time: float x = 10; for (i = 0; i < 1000; i++) x += 0.05; This is much better but can still have a problem with the addition: float x = 10; for (i = 0; i < 1000; i++) float x1 = x + (i * 0.05);
Tim Smith I'm going to patent thought. I have yet to see any prior art.
Tim Smith wrote:
it will tell you that floating point addition is inherently more imprecise that floating point multiplication
It works only if one of the term of the multiplication is an integer. :~ I understand it's like incertitude calculation, for addition you sum absolute incertitudes, for multiplication you sum relative ones.
Where do you expect us to go when the bombs fall?
-
<Using MFC>
double dValue = atof("0.1"); ASSERT(dValue == 0.1); double dSecondValue = (1 + dValue + dValue + dValue + dValue); ASSERT(dSecondValue == 1.4); // Crash
Where do you expect us to go when the bombs fall?
If you've ever studied the harmonic series (1 + 1/2 + 1/3 + ...), you'll know that it diverges very slowly. However, if you use any calculator to sum it up, you'll think that it converges. When I was still a pre-engineering student, before I switched to Math, we were taught to start any summand from the lower end and add up the bigger numbers so that the round-off error is minimized. In this case, you need to add the 1 last, but not after subtracting the integer part, multiplying the decimal part by a power of ten and rounding it to see if it is still zero or one depending on what the value is exactly and then doing the compare. Since everybody here has been pulling your leg and giving you grief, I'll keep my jokes to myself. Yeah, I don't have any anyway. :) It's annoying as a developer that you actually have to write a check to make sure your numbers are what they should be. Makes you wonder why we use machines for calculations and why cororations aren't going broke because of them. Hmmm, maybe they round to their benefit and that's why people are going broke and they're doing very well. :rolleyes: I'm in the wrong business.
"This perpetual motion machine she made is a joke. It just keeps going faster and faster. Lisa, get in here! In this house, we obey the laws of thermodynamics!" - Homer Simpson Web - Blog - RSS - Math - LinkedIn - BM
-
A) Never hate B) Don't expect them to do what they can't
-
I really do think the compiler should throw an error when you try to compare floating point values for equality.
cheers, Chris Maunder
CodeProject.com : C++ MVP
Chris Maunder wrote:
I really do think the compiler should throw an error when you try to compare floating point values for equality.
It seems to me that a data type where the concept of equal values is either undefined or can't be practically determined is clearly "half-baked".
-
<Using MFC>
double dValue = atof("0.1"); ASSERT(dValue == 0.1); double dSecondValue = (1 + dValue + dValue + dValue + dValue); ASSERT(dSecondValue == 1.4); // Crash
Where do you expect us to go when the bombs fall?
-
Chris Maunder wrote:
I really do think the compiler should throw an error when you try to compare floating point values for equality.
It seems to me that a data type where the concept of equal values is either undefined or can't be practically determined is clearly "half-baked".
You think maybe we should instead store the value as "one-point-five"? Or even "three-point-one-four-one-five-nine..."?
cheers, Chris Maunder
CodeProject.com : C++ MVP
-
You think maybe we should instead store the value as "one-point-five"? Or even "three-point-one-four-one-five-nine..."?
cheers, Chris Maunder
CodeProject.com : C++ MVP
Chris Maunder wrote:
You think maybe we should instead store the value as "one-point-five"? Or even "three-point-one-four-one-five-nine..."?
Ha, ha. Actually, Plain English uses ratios for both rational numbers and for reasonable approximations of irrational numbers (curious name, don't you think?). For example, we typically use 355/113 for pi. It has been our experience that rounding errors can be more easily minimized with the ratio approach than floating-point numbers. Plain English also supports scaled integers which, on a 32-bit machine, are sufficient for all but the most demanding problems and which, on a 64-bit machine, should suffice for nearly everything else. In either case, the concept of "equal values" can be defined and implemented with rigor, consistency, and reliability. These are desirable things, yes? And since adopting this approach eliminates an entire processor from the machine, it appears to be a significantly more efficient approach, as well. Not to mention less noisy. Our objections to "floating point" or "real" numbers - besides those stated above - are two. First, they suggest a "continuous" universe, rather than a discrete one. Yet we know that electrons are never between shells and that that famous arrow really does get where it's going. See [^] for a digital view of the universe. Secondly, as popular as the metric system may be in many countries, we find it much less effective in everyday life than the English system. If you're not hungry enough for a whole piece of pie, for example, do you typically ask for a half, or a tenth? Do you double an estimate you're not sure about, or multiply it by ten? In other words, people - who have not been trained otherwise - naturally think in halves and wholes, not tenths and hundredths.
-
Chris Maunder wrote:
You think maybe we should instead store the value as "one-point-five"? Or even "three-point-one-four-one-five-nine..."?
Ha, ha. Actually, Plain English uses ratios for both rational numbers and for reasonable approximations of irrational numbers (curious name, don't you think?). For example, we typically use 355/113 for pi. It has been our experience that rounding errors can be more easily minimized with the ratio approach than floating-point numbers. Plain English also supports scaled integers which, on a 32-bit machine, are sufficient for all but the most demanding problems and which, on a 64-bit machine, should suffice for nearly everything else. In either case, the concept of "equal values" can be defined and implemented with rigor, consistency, and reliability. These are desirable things, yes? And since adopting this approach eliminates an entire processor from the machine, it appears to be a significantly more efficient approach, as well. Not to mention less noisy. Our objections to "floating point" or "real" numbers - besides those stated above - are two. First, they suggest a "continuous" universe, rather than a discrete one. Yet we know that electrons are never between shells and that that famous arrow really does get where it's going. See [^] for a digital view of the universe. Secondly, as popular as the metric system may be in many countries, we find it much less effective in everyday life than the English system. If you're not hungry enough for a whole piece of pie, for example, do you typically ask for a half, or a tenth? Do you double an estimate you're not sure about, or multiply it by ten? In other words, people - who have not been trained otherwise - naturally think in halves and wholes, not tenths and hundredths.
There are an awful lot of irrational numbers out there. I think I would rather have my planes and bridges built using a floating point approximation of PI rather than 355/113 >In either case, the concept of "equal values" can be defined and implemented with rigor, consistency, and reliability. It's a pity these concepts don't actually appear in real life. You postulate that the universe isn't continous but is discrete, implying you believ in quantum theory, yet the basis of quantum theory itself is that there is an inherent uncertainty in all measurements. >Secondly, as popular as the metric system may be in many countries, we find it much less effective in everyday life than the English system. That's because you live in a country that uses the Imperial system. I buy food that is weighed in grams, and buy petrol and milk in litres, and need to know how many kilometres there are till my turnoff. If I ever talk in halves or quarters I mean it in a vague way ("half a loaf of bread, please") and there is no need for accuracy.
cheers, Chris Maunder
CodeProject.com : C++ MVP
-
There are an awful lot of irrational numbers out there. I think I would rather have my planes and bridges built using a floating point approximation of PI rather than 355/113 >In either case, the concept of "equal values" can be defined and implemented with rigor, consistency, and reliability. It's a pity these concepts don't actually appear in real life. You postulate that the universe isn't continous but is discrete, implying you believ in quantum theory, yet the basis of quantum theory itself is that there is an inherent uncertainty in all measurements. >Secondly, as popular as the metric system may be in many countries, we find it much less effective in everyday life than the English system. That's because you live in a country that uses the Imperial system. I buy food that is weighed in grams, and buy petrol and milk in litres, and need to know how many kilometres there are till my turnoff. If I ever talk in halves or quarters I mean it in a vague way ("half a loaf of bread, please") and there is no need for accuracy.
cheers, Chris Maunder
CodeProject.com : C++ MVP
Chris Maunder wrote:
I think I would rather have my planes and bridges built using a floating point approximation of PI rather than 355/113
Moot point. There's a ratio for whatever degree of precision you desire. But if you're willing to trust your planes and bridges to floating point calculations, why not your money?
Chris Maunder wrote:
It's a pity these concepts don't actually appear in real life.
The concept I was referring to was "the equality of values in a given data type". Which can be acheived - with enough rigor, consistency, and reliablilty for practical use - in spite of any quantum uncertainty.
Chris Maunder wrote:
If I ever talk in halves or quarters I mean it in a vague way ("half a loaf of bread, please") and there is no need for accuracy.
But do you ever say, "A tenth of a loaf of bread, please"? I think not, because when a whole loaf is too much, your next thought - even though you (apparently) were not brought up on the Imperial system - is half a loaf, not 1/10. It's only human. Another curious example is how Americans, even though our dollars are divided into 100ths, consistently and persistently use phrases like "a half dollar" or "a quarter" - instead of the metric "fifty cent piece" or "twenty-five cent piece".
-
Chris Maunder wrote:
I think I would rather have my planes and bridges built using a floating point approximation of PI rather than 355/113
Moot point. There's a ratio for whatever degree of precision you desire. But if you're willing to trust your planes and bridges to floating point calculations, why not your money?
Chris Maunder wrote:
It's a pity these concepts don't actually appear in real life.
The concept I was referring to was "the equality of values in a given data type". Which can be acheived - with enough rigor, consistency, and reliablilty for practical use - in spite of any quantum uncertainty.
Chris Maunder wrote:
If I ever talk in halves or quarters I mean it in a vague way ("half a loaf of bread, please") and there is no need for accuracy.
But do you ever say, "A tenth of a loaf of bread, please"? I think not, because when a whole loaf is too much, your next thought - even though you (apparently) were not brought up on the Imperial system - is half a loaf, not 1/10. It's only human. Another curious example is how Americans, even though our dollars are divided into 100ths, consistently and persistently use phrases like "a half dollar" or "a quarter" - instead of the metric "fifty cent piece" or "twenty-five cent piece".
The Grand Negus wrote:
Moot point. There's a ratio for whatever degree of precision you desire.
Are you seriously suggesting that all numerical calculations, eg numerical modelling, be done by first generating ratios for every floating point value needed, to a degree of accuracy that is as good as, or better than current floating point accuracy, and then propogate those fractions throughout the entire set of calculations? Why? What will it save you? You're not going to gain accuracy because you've already made an appoximation. You're going to have all sorts of problems with overflow. And in the end the value you give back to the person modelling, say, forces in a wingspan isn't going to be 982349587/6834567 Nm^2, it's going to be 143.73. That's an awfully large roundabout you're taking to come up with the same answer.
The Grand Negus wrote:
But do you ever say, "A tenth of a loaf of bread, please"? I think not,
No, but every time I go to the butchers I ask for 200g of sliced ham. If your computations-using-fractions works for you then perfect. You may consider floating point storage a bad solution, and you've offered an alternate which is commendable. But I honestly do not think it's practical. Not for the things such as forecasting weather or perform amazing feats of engineering.
cheers, Chris Maunder
CodeProject.com : C++ MVP