470,600 Members | 1,466 Online

# converting float to double

Recently in our code, I ran into a situation where were stuffing a
float inside a double. The precision was extended automatically
because of that. To make a long story short, this caused problems
elsewhere in another part of the system where that figure was used for
some calculation and some eventual truncation led to the system going
haywire. So my question is, given this code:

int main()
{
float f = 59.89F;
/* the following line caused problems */
double d = f;

/* they ended up solving it like this */
f *= 1000.0;
double dd = (double)f / 1000.0;

return 0;
}

I see what is being done but why does the latter make the situation
better?
(consider 'f' in real life to hold stock prices)

Dec 19 '06 #1
116 31893 Dilip wrote:
Recently in our code, I ran into a situation where were stuffing a
float inside a double. The precision was extended automatically
because of that. To make a long story short, this caused problems
elsewhere in another part of the system where that figure was used for
some calculation and some eventual truncation led to the system going
haywire. So my question is, given this code:

int main()
{
float f = 59.89F;
/* the following line caused problems */
double d = f;

/* they ended up solving it like this */
f *= 1000.0;
double dd = (double)f / 1000.0;

return 0;
}

I see what is being done but why does the latter make the situation
better?
(consider 'f' in real life to hold stock prices)
Not a good idea...

Floating point is by its nature imprecise - multiplying by 1000 simply
adjusts the imprecision in a way which was helpful in this specific
case, but may not be helpful in all cases (AFAIK).

If you want to safely and accurately handle this sort of data, sources
such as http://www2.hursley.ibm.com/decimal/ may be helpful.

Dec 19 '06 #2
Dilip wrote:
>
Recently in our code, I ran into a situation where were stuffing a
float inside a double. The precision was extended automatically
because of that. To make a long story short, this caused problems
elsewhere in another part of the system where that figure was used
for some calculation and some eventual truncation led to the system
going haywire. So my question is, given this code:

int main()
{
float f = 59.89F;
/* the following line caused problems */
double d = f;

/* they ended up solving it like this */
f *= 1000.0;
double dd = (double)f / 1000.0;

return 0;
}

I see what is being done but why does the latter make the
situation better?
(consider 'f' in real life to hold stock prices)
It doesn't. It should make it dead wrong. Either you are not
showing the real situation or you have a compiler bug.

Alternatively, if you are depending on exact values from floats
and/or doubles, your code is broken. Since stock prices (today)
are decimalized, just use a normalized integer of suitable size.
Read Knuth (TAOCP) on floating point and expected errors.

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
Dec 19 '06 #3
Dilip wrote:
>
Recently in our code, I ran into a situation where were stuffing a
float inside a double. The precision was extended automatically
because of that. To make a long story short, this caused problems
elsewhere in another part of the system where that figure was used for
some calculation and some eventual truncation led to the system going
haywire. So my question is, given this code:

int main()
{
float f = 59.89F;
/* the following line caused problems */
double d = f;

/* they ended up solving it like this */
f *= 1000.0;
double dd = (double)f / 1000.0;

return 0;
}

I see what is being done but why does the latter make the situation
better?
(consider 'f' in real life to hold stock prices)
I wouldn't have used a float variable like that to begin with.

Use float,
when you need the smallest floating point type.

Use long double,
when you need the floating point type
with most precision and or range.

Use double,
all the rest of the time.

--
pete
Dec 19 '06 #4
CBFalconer wrote:
Dilip wrote:

Recently in our code, I ran into a situation where were stuffing a
float inside a double. The precision was extended automatically
because of that. To make a long story short, this caused problems
elsewhere in another part of the system where that figure was used
for some calculation and some eventual truncation led to the system
going haywire. So my question is, given this code:

int main()
{
float f = 59.89F;
/* the following line caused problems */
double d = f;

/* they ended up solving it like this */
f *= 1000.0;
double dd = (double)f / 1000.0;

return 0;
}

I see what is being done but why does the latter make the
situation better?
(consider 'f' in real life to hold stock prices)

It doesn't. It should make it dead wrong. Either you are not
showing the real situation or you have a compiler bug.
I am showing exactly what is being done in our codebase. Why do you
say there is a compiler bug? If you try the example, as soon as the
1st line is executed the variable f contains 59.889999. After its
stuffed into double d, it becomes 59.889999389648437 (I know these
values vary after a certain decimal position). The
multiplication/division to 1000.0 however produces 59.890000000000001
in dd. The management somehow feels thats a more "accurate"
representation. I guess after rounding off to 2 decimal places they
expected 59.89 but a bug somewhere in the code ensured that the former
case got rounded off to 59.88. It was off by a penny and that
triggered major problems in other calculations.
Since stock prices (today)
are decimalized, just use a normalized integer of suitable size.
Read Knuth (TAOCP) on floating point and expected errors.
I am not exactly unfamiliar with floating point representations and its
associated accuracy related problems but I think this is the first time
I am working in a domain where such problems crop up on a more frequent
basis. Could you please elaborate on that "normalized integer of
suitable size"?

Dec 19 '06 #5
CBFalconer <cb********@yahoo.comwrites:
Dilip wrote:
>Recently in our code, I ran into a situation where were stuffing a
float inside a double. The precision was extended automatically
because of that. To make a long story short, this caused problems
elsewhere in another part of the system where that figure was used
for some calculation and some eventual truncation led to the system
going haywire. So my question is, given this code:

int main()
{
float f = 59.89F;
/* the following line caused problems */
double d = f;

/* they ended up solving it like this */
f *= 1000.0;
double dd = (double)f / 1000.0;

return 0;
}

I see what is being done but why does the latter make the
situation better?
(consider 'f' in real life to hold stock prices)

It doesn't. It should make it dead wrong. Either you are not
showing the real situation or you have a compiler bug.
Are you sure about that? The code multiplies f by 1000.0, then
divides the result by 1000.0. The *mathematical* result is the same,
but the way it's done is likely to change the rounding behavior
slightly. It's not likely to be a general solution to any problem.
It smacks of "cargo cult programming"; see
<http://www.catb.org/~esr/jargon/html/C/cargo-cult-programming.html>.
Alternatively, if you are depending on exact values from floats
and/or doubles, your code is broken. Since stock prices (today)
are decimalized, just use a normalized integer of suitable size.
Read Knuth (TAOCP) on floating point and expected errors.
Another good resource is Goldberg's paper "What Every Computer
floating" to find it.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Dec 19 '06 #6

pete wrote:
Dilip wrote:

Recently in our code, I ran into a situation where were stuffing a
float inside a double. The precision was extended automatically
because of that. To make a long story short, this caused problems
elsewhere in another part of the system where that figure was used for
some calculation and some eventual truncation led to the system going
haywire. So my question is, given this code:

int main()
{
float f = 59.89F;
/* the following line caused problems */
double d = f;

/* they ended up solving it like this */
f *= 1000.0;
double dd = (double)f / 1000.0;

return 0;
}

I see what is being done but why does the latter make the situation
better?
(consider 'f' in real life to hold stock prices)

I wouldn't have used a float variable like that to begin with.

Use float,
when you need the smallest floating point type.

Use long double,
when you need the floating point type
with most precision and or range.

Use double,
all the rest of the time.
This comes squarely under the heading "If you can understand this

- William Hughes
--
pete
Dec 19 '06 #7
2006-12-19 <45***********@mindspring.com>,
pete wrote:
Dilip wrote:
>>
Recently in our code, I ran into a situation where were stuffing a
float inside a double. The precision was extended automatically
because of that. To make a long story short, this caused problems
elsewhere in another part of the system where that figure was used for
some calculation and some eventual truncation led to the system going
haywire. So my question is, given this code:

int main()
{
float f = 59.89F;
/* the following line caused problems */
double d = f;

/* they ended up solving it like this */
f *= 1000.0;
double dd = (double)f / 1000.0;

return 0;
}

I see what is being done but why does the latter make the situation
better?
(consider 'f' in real life to hold stock prices)

I wouldn't have used a float variable like that to begin with.

Use float,
when you need the smallest floating point type.

Use long double,
when you need the floating point type
with most precision and or range.
Use integers,
when you need to be exact to within a known specific unit, such as
a dollar, cent, or Nth part of either.
Use double,
all the rest of the time.
Dec 19 '06 #8
Dilip wrote:
I am not exactly unfamiliar with floating point representations and its
associated accuracy related problems but I think this is the first time
I am working in a domain where such problems crop up on a more frequent
basis. Could you please elaborate on that "normalized integer of
suitable size"?
Have an integer variable [probably type long or long long] counting the
number of cents (or thousandths of a dollar, or whatever). Doing your
weird cast thing fixed the error in that one particular case, but could
have introduced other errors elsewhere.
Dec 19 '06 #9
Keith Thompson wrote:
CBFalconer <cb********@yahoo.comwrites:
Dilip wrote:
Recently in our code, I ran into a situation where were stuffing a
float inside a double. The precision was extended automatically
because of that. To make a long story short, this caused problems
elsewhere in another part of the system where that figure was used
for some calculation and some eventual truncation led to the system
going haywire. So my question is, given this code:

int main()
{
float f = 59.89F;
/* the following line caused problems */
double d = f;

/* they ended up solving it like this */
f *= 1000.0;
double dd = (double)f / 1000.0;

return 0;
}

I see what is being done but why does the latter make the
situation better?
(consider 'f' in real life to hold stock prices)
It doesn't. It should make it dead wrong. Either you are not
showing the real situation or you have a compiler bug.

Are you sure about that? The code multiplies f by 1000.0, then
divides the result by 1000.0. The *mathematical* result is the same,
but the way it's done is likely to change the rounding behavior
slightly. It's not likely to be a general solution to any problem.
It smacks of "cargo cult programming"; see
<http://www.catb.org/~esr/jargon/html/C/cargo-cult-programming.html>.
I totally agree which is the reason why I posted to this NG to
understand what exactly is being solved by that 1000.0 workaround.
Another good resource is Goldberg's paper "What Every Computer
floating" to find it.
I came across that paper quite a few times in the past. I never
realized one day I would need to know that amount of detail. Do you
have any insights into that normalized integer thing everyone has been
pointing out so far? If one of you can give me an example, i'd
appreciate it.

Dec 19 '06 #10
Dilip wrote:
I came across that paper quite a few times in the past. I never
realized one day I would need to know that amount of detail. Do you
have any insights into that normalized integer thing everyone has been
pointing out so far? If one of you can give me an example, i'd
appreciate it.
Store the amount as pennies in a long long integer. The Goldberg paper
is a must read if you are doing numerics with other people's money.
IMO-YMMV.

#include <stdio.h>
#include <stdlib.h>

char string;
int main(void)
{
long long pennies;

try_again:
puts("Enter a cost in pennies:");
if (fgets(string, sizeof string, stdin)) {
sscanf(string, "%lld", &pennies);
printf("That's %lld dollars and %lld cents or \$%lld.%lld\n",
pennies / 100, pennies % 100, pennies / 100, pennies % 100);
} else
goto try_again;

return EXIT_SUCCESS;
}
/* e.g.:
Enter a cost in pennies:
99999999
That's 999999 dollars and 99 cents or \$999999.99
*/

Dec 19 '06 #11

"Dilip" <rd*****@lycos.comwrote in message
>
Recently in our code, I ran into a situation where were stuffing a
float inside a double. The precision was extended automatically
because of that. To make a long story short, this caused problems
elsewhere in another part of the system where that figure was used for
some calculation and some eventual truncation led to the system going
haywire. So my question is, given this code:

int main()
{
float f = 59.89F;
/* the following line caused problems */
double d = f;

/* they ended up solving it like this */
f *= 1000.0;
double dd = (double)f / 1000.0;

return 0;
}

I see what is being done but why does the latter make the situation
better?
(consider 'f' in real life to hold stock prices)
That is called hacking.
Somehow a cent must have been knocked off in the code - decimals cannot be
represented perfectly in binary floating point notation - leading the
program to suspect salami tactics. Multiplying by 1000 converts the number
to an integer, and the division is done in double precision, so the cent
might not have been dropped.

This approach to problem solving, incidentally, is known as hacking, and is
an indication that the company's management is in a serious mess.
--
www.personal.leeds.ac.uk/~bgy1mm

Dec 19 '06 #12
Dilip wrote:
Keith Thompson wrote:
>CBFalconer <cb********@yahoo.comwrites:
>>Dilip wrote:

Recently in our code, I ran into a situation where were stuffing a
float inside a double. The precision was extended automatically
because of that. To make a long story short, this caused problems
elsewhere in another part of the system where that figure was used
for some calculation and some eventual truncation led to the system
going haywire. So my question is, given this code:

int main()
{
float f = 59.89F;
/* the following line caused problems */
double d = f;

/* they ended up solving it like this */
f *= 1000.0;
double dd = (double)f / 1000.0;

return 0;
}

I see what is being done but why does the latter make the
situation better?
(consider 'f' in real life to hold stock prices)

It doesn't. It should make it dead wrong. Either you are not
showing the real situation or you have a compiler bug.

Are you sure about that? The code multiplies f by 1000.0, then
divides the result by 1000.0. The *mathematical* result is the same,
but the way it's done is likely to change the rounding behavior
slightly. It's not likely to be a general solution to any problem.
It smacks of "cargo cult programming"; see
<http://www.catb.org/~esr/jargon/html/C/cargo-cult-programming.html>.

I totally agree which is the reason why I posted to this NG to
understand what exactly is being solved by that 1000.0 workaround.
>Another good resource is Goldberg's paper "What Every Computer
floating" to find it.

I came across that paper quite a few times in the past. I never
realized one day I would need to know that amount of detail. Do you
have any insights into that normalized integer thing everyone has been
pointing out so far? If one of you can give me an example, i'd
appreciate it.
As a temporary measure, try converting the value to a long:

long longvalueincents;

longvalueincents = (f * 100) + 0.5; /* control rounding */

and, to print it out, try:

printf("%ld.%ld", longvalueincents/100, longvalueincents%100);
fflush(stdout);

Note that you don't need any casts. A cast is probably an error.

You can probably incorporate all this in a single function that
dumps the double rounded to two decimals, but reading the printf
documentation is likely to be more rewarding.

You could also hire me. :-) See below.

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
Dec 19 '06 #13

<dc*****@connx.comwrote in message
Dilip wrote:
>I came across that paper quite a few times in the past. I never
realized one day I would need to know that amount of detail. Do you
have any insights into that normalized integer thing everyone has been
pointing out so far? If one of you can give me an example, i'd
appreciate it.

Store the amount as pennies in a long long integer. The Goldberg paper
is a must read if you are doing numerics with other people's money.
IMO-YMMV.
Or in a double, but as an integral number of pence.
That way the maths is accurate up to 48 bits or so, and degrades garcefully
under hyperinflation.
--
www.personal.leeds.ac.uk/~bgy1mm

Dec 20 '06 #14

Dilip wrote:
Recently in our code, I ran into a situation where were stuffing a
float inside a double. The precision was extended automatically
because of that. To make a long story short, this caused problems
elsewhere in another part of the system where that figure was used for
some calculation and some eventual truncation led to the system going
haywire. So my question is, given this code:

int main()
{
float f = 59.89F;
/* the following line caused problems */
double d = f;

/* they ended up solving it like this */
f *= 1000.0;
double dd = (double)f / 1000.0;

return 0;
}

I see what is being done but why does the latter make the situation
better?
(consider 'f' in real life to hold stock prices)
For the purposes of this explanation treat double as exact
and float as approximate.

The problem is that most exact decimal fractions cannot
be represented exactly by floats. So you need to use
a value that is just a little above or just a little below.

Suppose we have

f=0.0001

then f will have a value slightly below 0.0001
(or maybe not, but it did on my machine just now).

Now multiply by 1000
Now f will have to approximate 0.1. The best approximation is
a value slightly above 0.1 (at least on my machine just now)
So after

1000*f

we get a value slightly above 0.1. We now assign to a double

dd=1000*f

(This is done by first calculating 1000*f as a float and then
assigning to double). We next divide dd by 1000.
dd now has a value slightly greater than 0.0001.

So the net effect of multiplying by 1000, storing in a double, then
dividing by 1000, is to convert a value slightly below 0.0001 to a
value
slightly above 0.0001. Because the code is clearly incompetently
written, it will later blindly truncate, converting a miniscule
difference into
a noticable one.

Of course if you start out with some other value for f, something
different
may happen. Something different may happen on different machines,
or with different compilers, or with different compiler options, or on
different days, or on different runs, or at different times within the
same run.

I wouldn't trust this code in a rigged demo.

- William Hughes.

Dec 20 '06 #15
dc*****@connx.com wrote:
printf("That's %lld dollars and %lld cents or \$%lld.%lld\n",
pennies / 100, pennies % 100, pennies / 100, pennies % 100);
printf("That's %lld dollars and %lld cents or \$%lld.%02lld\n",
pennies / 100, pennies % 100, pennies / 100, pennies % 100);
--
Dec 20 '06 #16
Recently in our code, I ran into a situation where were stuffing a
float inside a double. The precision was extended automatically
because of that. To make a long story short, this caused problems
elsewhere in another part of the system where that figure was used for
some calculation and some eventual truncation led to the system going
haywire. So my question is, given this code:
....
float f = 59.89F;
/* the following line caused problems */
double d = f;
d will be approximately equal to 59.89 with the same precision as f
/* they ended up solving it like this */
f *= 1000.0;
By sheer luck (due to the rounding rules) f is exactly equal to 59890.
(You could even use 100 here with exactly the same results.)
double dd = (double)f / 1000.0;
And so this is a better approximation in double precision of 59.89.
I see what is being done but why does the latter make the situation
better?
It is only luck. For other values of f it could fail and dd could
(I think) even be worse than d.
(consider 'f' in real life to hold stock prices)
Consider not to use floating point for stock prices. Floating point
inherently carries imprecision (especially if you want to do decimal
calculations), which you do not want with stock prices.
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
Dec 20 '06 #17
....
I am showing exactly what is being done in our codebase. Why do you
say there is a compiler bug?
There is indeed none, but you are misunderstanding floating point.
If you try the example, as soon as the
1st line is executed the variable f contains 59.889999. After its
stuffed into double d, it becomes 59.889999389648437 (I know these
values vary after a certain decimal position).
They do not vary, but you should print with sufficient precision.
The exact value stored in both f and d is:
59.8899993896484375
The
multiplication/division to 1000.0 however produces 59.890000000000001
in dd.
Actually the value stored in dd is:
59.8900000000000005684341886080801486968994140625
But as I stated in another article, it is only luck that this value is
larger than 59.89.
The management somehow feels thats a more "accurate"
representation. I guess after rounding off to 2 decimal places they
expected 59.89 but a bug somewhere in the code ensured that the former
case got rounded off to 59.88.
Probably not rounding was done but truncation. In that case
when d * 100 is truncated to int it will indeed give 59.88 and
dd * 100 is truncated to 59.89. I can come up with initial values
where this is all reversed. And it is even platform dependent.
in comp.arch.arithmetic and the associated thread.
It was off by a penny and that
triggered major problems in other calculations.
If off by a penny triggers major problems, you should *really* consider
using integer arithmetic in pennies.
Since stock prices (today)
are decimalized, just use a normalized integer of suitable size.
Read Knuth (TAOCP) on floating point and expected errors.

I am not exactly unfamiliar with floating point representations and its
associated accuracy related problems but I think this is the first time
I am working in a domain where such problems crop up on a more frequent
basis. Could you please elaborate on that "normalized integer of
suitable size"?
Use 64 bit integers for the amounts in pennies. When you use floating
point and store the amount in dollars you should not be surprised that
after a number of calculations the result is off by a penny (or perhaps
more). Floating point variables can not store amounts in dollars with
precision upto pennies.
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
Dec 20 '06 #18
"Malcolm" <re*******@btinternet.comwrites:
<dc*****@connx.comwrote in message
>Dilip wrote:
>>I came across that paper quite a few times in the past. I never
realized one day I would need to know that amount of detail. Do you
have any insights into that normalized integer thing everyone has been
pointing out so far? If one of you can give me an example, i'd
appreciate it.

Store the amount as pennies in a long long integer. The Goldberg paper
is a must read if you are doing numerics with other people's money.
IMO-YMMV.
Or in a double, but as an integral number of pence.
That way the maths is accurate up to 48 bits or so, and degrades garcefully
under hyperinflation.
For certain values of "gracefully".

In some contexts, I would think that a one-penny error in a
multi-trillion pound calculation ("trillion" meaning 10**12) would be
considered unacceptable.

64-bit integers should be more than enough to handle anything short of
really extreme hyperinflation with no errors -- assuming that
addition, subtraction, and multiplication by whole numbers are the
only required operations.

Compound interest introduces some interesting problems, but I think
there are very specific rules for how to do those calculations. I
suspect floating-point would not implement those rules correctly.

Warning: I'm musing about stuff I've heard about here and there, but
in which I have no particular expertise.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Dec 20 '06 #19
"Dik T. Winter" <Di********@cwi.nlwrites:
[...]
Consider not to use floating point for stock prices. Floating point
inherently carries imprecision (especially if you want to do decimal
calculations), which you do not want with stock prices.
If you need to do calculations with stock prices, find out what the
formal rules are for doing those calculations. (I have no idea what
those rules are, or where you'd find out about them.) Once you've
done that, implement those rules. ("How?" "Correctly.")

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Dec 20 '06 #20
Dik T. Winter wrote:
(consider 'f' in real life to hold stock prices)

Consider not to use floating point for stock prices. Floating point
inherently carries imprecision (especially if you want to do decimal
calculations), which you do not want with stock prices.
THis isn't always possible. The market data vendor I connect to
insists that I read the stock prices as float. So when I reach into
the byte-stream I will have to memcpy sizeof(float) number of bytes.
Since everywhere else the system uses double to hold these prices, I
had no choice but to stuff that retreived float value into a double
causing all these problems.

Dec 20 '06 #21
Dilip wrote:
Dik T. Winter wrote:
> (consider 'f' in real life to hold stock prices)

Consider not to use floating point for stock prices. Floating point
inherently carries imprecision (especially if you want to do decimal
calculations), which you do not want with stock prices.

THis isn't always possible. The market data vendor I connect to
insists that I read the stock prices as float.
Get a new vendor?
So when I reach into the byte-stream I will have to memcpy
sizeof(float) number of bytes. Since everywhere else the system uses
double to hold these prices, I had no choice but to stuff that
retreived float value into a double causing all these problems.
Well - if you can't get a better vendor, the best way would be to do this:

#include <math.h>
//...
float floating_price;
long int price_cents;
//...
price_cents = round(f*100.);

That way you'll actually get the value to the nearest cent, instead of
doing "voodoo magic" that happens to get the right answer in one
specific case you tested it on.

If you need tenths of a cent, use 1000. there, etc, whatever precision
you need. From that point, use the integer representation throughout
Dec 20 '06 #22
Random832 <ra****@random.yi.orgwrites:
Well - if you can't get a better vendor, the best way would be to do this:

#include <math.h>
//...
float floating_price;
long int price_cents;
//...
price_cents = round(f*100.);
For what it's worth, the round function is new in C99.
--
"IMO, Perl is an excellent language to break your teeth on"
--Micah Cowan
Dec 20 '06 #23
Dilip wrote:
Dik T. Winter wrote:
>Consider not to use floating point for stock prices.

THis isn't always possible. The market data vendor I connect to
insists that I read the stock prices as float. So when I reach into
the byte-stream I will have to memcpy sizeof(float) number of bytes.
Since everywhere else the system uses double to hold these prices, I
had no choice but to stuff that retreived float value into a double
causing all these problems.
Putting float values into doubles probably isn't the cause of your
problems. A double will hold exactly the same value as the original
float.

In most implementations, both float and double represent numbers as

(1 + m / 2^b) * 2^e

where m is (part of) the mantissa, b is the number of bits in m, e is
the exponent, and '^' denotes exponentiation. b is 23 for floats and 53
for doubles. Given the m, b, e of a float, you get the double with

m <<= 30
b += 30
e = e

Or to put it another way,

float: 59.89 - (1 + 7311196 / 8388608) * 32
double: 59.89 - (1 + (7311196 << 30) / (8388608 << 30)) * 32

The values are identical, so the problems must be arising from what is
being done with them.

- Ernie http://home.comcast.net/~erniew

Dec 20 '06 #24

Ernie Wright wrote:
Dilip wrote:
Dik T. Winter wrote:
Consider not to use floating point for stock prices.
THis isn't always possible. The market data vendor I connect to
insists that I read the stock prices as float. So when I reach into
the byte-stream I will have to memcpy sizeof(float) number of bytes.
Since everywhere else the system uses double to hold these prices, I
had no choice but to stuff that retreived float value into a double
causing all these problems.

Putting float values into doubles probably isn't the cause of your
problems. A double will hold exactly the same value as the original
float.
True, but irrelevent.

Mathematically

(float) (1000.0 * f) =/= 1000.0 * f

There is a small bit of rounding. So

(double)( (float) 1000.0*f)/ 1000.0 =/= f

In the present case we start out with a value just less than a multiple
of
a whole cent, and end up with a value just greater than a multiple
of a cent.

- William Hughes

Dec 20 '06 #25

Dilip wrote:
Dik T. Winter wrote:
(consider 'f' in real life to hold stock prices)
Consider not to use floating point for stock prices. Floating point
inherently carries imprecision (especially if you want to do decimal
calculations), which you do not want with stock prices.

THis isn't always possible. The market data vendor I connect to
insists that I read the stock prices as float. So when I reach into
the byte-stream I will have to memcpy sizeof(float) number of bytes.
Since everywhere else the system uses double to hold these prices, I
had no choice but to stuff that retreived float value into a double
causing all these problems.
A system that uses floating point variables to store prices should
work correctly if a price is just above or just below the
correct price (e.g for a price of 0.010001 dollars or 0.009998
dollars).
The system you use does not have this property. It needs
to be fixed. However, it appears that by management fiat you have
to use this system, and any problems are deemed yours.

A possible workaround:

Round to the nearest cent or tenth of a cent (*)
Convert to double
Add a fudge factor of 0.00000001

Now your prices will be subtly wrong, but they will be subtly wrong in
the correct direction. This should work as well as the
(double)(f*1000.0)/1000.0
kludge, and will work for any value. (adding the fudge factor may or
may
not be necessary. It would not be necessary, but for the fact that you
have to use a broken system. There may be a price that when expressed
as a double is slightly below the true price.)

* if you don't have access to a C99 type round() function, roll your
own
(e.g. floor(100.0*dd + 0.5)/100.0) or sprintf the value and sscanf
it back

- William
Hughes

Dec 20 '06 #26
Dilip wrote:
Dik T. Winter wrote:
>"Dilip" <rd*****@lycos.comwrites:
>>(consider 'f' in real life to hold stock prices)

Consider not to use floating point for stock prices. Floating
point inherently carries imprecision (especially if you want to
do decimal calculations), which you do not want with stock prices.

THis isn't always possible. The market data vendor I connect to
insists that I read the stock prices as float. So when I reach
into the byte-stream I will have to memcpy sizeof(float) number of
bytes. Since everywhere else the system uses double to hold these
prices, I had no choice but to stuff that retreived float value
into a double causing all these problems.
This is inherently impossible. While some systems use IEEE float
format, many do not, so you don't even know the bit pattern coming
in. Are you sure you aren't reading and converting a text stream?
If so all you have to do is write a suitable parser for a
particular text format, say dddd.dd, where d is a digit.

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
Dec 20 '06 #27
Ben Pfaff wrote:
Random832 <ra****@random.yi.orgwrites:
>Well - if you can't get a better vendor, the best way would be
to do this:

#include <math.h>
//...
float floating_price;
long int price_cents;
//...
price_cents = round(f*100.);

For what it's worth, the round function is new in C99.
and unnecessary.

price_cents = 100 * floating_price + 0.5;

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
Dec 20 '06 #28
2006-12-20 <45***************@yahoo.com>,
CBFalconer wrote:
Ben Pfaff wrote:
>Random832 <ra****@random.yi.orgwrites:
>>Well - if you can't get a better vendor, the best way would be
to do this:

#include <math.h>
//...
float floating_price;
long int price_cents;
//...
price_cents = round(f*100.);

For what it's worth, the round function is new in C99.

and unnecessary.

price_cents = 100 * floating_price + 0.5;
I thought that was implementation-defined.
Dec 20 '06 #29

Random832 wrote:
2006-12-20 <45***************@yahoo.com>,
CBFalconer wrote:
Ben Pfaff wrote:
Random832 <ra****@random.yi.orgwrites:

Well - if you can't get a better vendor, the best way would be
to do this:

#include <math.h>
//...
float floating_price;
long int price_cents;
//...
price_cents = round(f*100.);

For what it's worth, the round function is new in C99.
and unnecessary.

price_cents = 100 * floating_price + 0.5;

I thought that was implementation-defined.
Not if price_cents is assumed to be greater than or equal to 0.

- William Hughes

Dec 20 '06 #30
William Hughes wrote:
Ernie Wright wrote:
>Putting float values into doubles probably isn't the cause of your
problems. A double will hold exactly the same value as the original
float.

True, but irrelevent.
When someone writes that "stuffing a float value into a double causes
all these problems," it's perfectly relevant to tell them that's not the
case. Or to put it another way, if the OP changed all the doubles to
float, the problems would be exactly the same.
(double)( (float) 1000.0*f)/ 1000.0 =/= f

In the present case we start out with a value just less than a
multiple of a whole cent, and end up with a value just greater than a
multiple of a cent.
That wasn't the problem the OP was referring to. It was a "solution."

The real problem is the truncation, mentioned by the OP in his first
post but not shown in code. The truncation is where the code is broken.

- Ernie http://home.comcast.net/~erniew

Dec 20 '06 #31
CBFalconer wrote:
Ben Pfaff wrote:
>For what it's worth, the round function is new in C99.

and unnecessary.

price_cents = 100 * floating_price + 0.5;
which is only one of several widely used rounding rules, and not
necessarily the one preferred for financial transactions.

- Ernie http://home.comcast.net/~erniew

Dec 20 '06 #32
CBFalconer wrote:
While some systems use IEEE float format, many do not,
In worlds where "some" = 99% and "many" = 1%.

- Ernie http://home.comcast.net/~erniew

Dec 20 '06 #33

Ernie Wright wrote:
CBFalconer wrote:
Ben Pfaff wrote:
For what it's worth, the round function is new in C99.
and unnecessary.

price_cents = 100 * floating_price + 0.5;

which is only one of several widely used rounding rules, and not
necessarily the one preferred for financial transactions.
The problem is "take a price, assumed to be an integer
number of cents, expressed as a floating
point number in dollars, and convert it to an integer number
of cents".

The expression given does this correctly. That it might not be
the correct solution for some other problem is irrelevent.
- William Hughes

Dec 20 '06 #34

CBFalconer wrote:
Ben Pfaff wrote:
Random832 <ra****@random.yi.orgwrites:
Well - if you can't get a better vendor, the best way would be
to do this:

#include <math.h>
//...
float floating_price;
long int price_cents;
//...
price_cents = round(f*100.);
For what it's worth, the round function is new in C99.

and unnecessary.

price_cents = 100 * floating_price + 0.5;
Almost:
>From the C-FAQ:
14.6: How do I round numbers?

A: The simplest and most straightforward way is with code like

(int)(x + 0.5)

This technique won't work properly for negative numbers,
though (for which you could use something like
(int)(x < 0 ? x - 0.5 : x + 0.5)).

Also, most banking institutions will prefer banker's rounding to simple
rounding.

Dec 20 '06 #35
In article <87************@blp.benpfaff.orgbl*@cs.stanford.edu writes:
Random832 <ra****@random.yi.orgwrites:
Well - if you can't get a better vendor, the best way would be to do this:

#include <math.h>
//...
float floating_price;
long int price_cents;
//...
price_cents = round(f*100.);

For what it's worth, the round function is new in C99.
For this kind of data, (int)ftrunc(f * 100 + 0.5) works just as well.
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
Dec 21 '06 #36
In article <11**********************@i12g2000cwa.googlegroups .com"William Hughes" <wp*******@hotmail.comwrites:
Dilip wrote:
....
THis isn't always possible. The market data vendor I connect to
insists that I read the stock prices as float. So when I reach into
the byte-stream I will have to memcpy sizeof(float) number of bytes.
Since everywhere else the system uses double to hold these prices, I
had no choice but to stuff that retreived float value into a double
causing all these problems.

A system that uses floating point variables to store prices should
work correctly if a price is just above or just below the
correct price (e.g for a price of 0.010001 dollars or 0.009998
dollars).
The system you use does not have this property. It needs
to be fixed.
How do you fix a system that follows the C standard?
A possible workaround:
Round to the nearest cent or tenth of a cent (*)
Convert to double
Add a fudge factor of 0.00000001
Now your prices will be subtly wrong, but they will be subtly wrong in
the correct direction. This should work as well as the
(double)(f*1000.0)/1000.0
kludge, and will work for any value.
But after subtraction you can be subtly wrong the wrong way. To avoid
all this is done by internally working with integers, longs, long longs,
or whatever integer size you need.
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
Dec 21 '06 #37
CBFalconer <cb********@yahoo.comwrites:
Dilip wrote:
>Dik T. Winter wrote:
>>"Dilip" <rd*****@lycos.comwrites:

(consider 'f' in real life to hold stock prices)

Consider not to use floating point for stock prices. Floating
point inherently carries imprecision (especially if you want to
do decimal calculations), which you do not want with stock prices.

THis isn't always possible. The market data vendor I connect to
insists that I read the stock prices as float. So when I reach
into the byte-stream I will have to memcpy sizeof(float) number of
bytes. Since everywhere else the system uses double to hold these
prices, I had no choice but to stuff that retreived float value
into a double causing all these problems.

This is inherently impossible. While some systems use IEEE float
format, many do not, so you don't even know the bit pattern coming
in. Are you sure you aren't reading and converting a text stream?
If so all you have to do is write a suitable parser for a
particular text format, say dddd.dd, where d is a digit.
It's inherently possible to do this portably.

My guess (and it's only a guess) is that his vendor is providing raw
data of type "float", probably in 32-bit IEEE format in some
particular byte order. If Dilip has already been successful in
storing this incoming data in objects of type "float", then that part
of the problem is solved.

The remaining problem is what to do with the information once he has
it.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Dec 21 '06 #38
"Dik T. Winter" <Di********@cwi.nlwrites:
In article <87************@blp.benpfaff.orgbl*@cs.stanford.edu writes:
Random832 <ra****@random.yi.orgwrites:
price_cents = round(f*100.);
For what it's worth, the round function is new in C99.
For this kind of data, (int)ftrunc(f * 100 + 0.5) works just as well.
I can't find any mention of ftrunc, in C99 or even in SUSv3.
--
int main(void){char p[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv wxyz.\
\n",*q="kl BIcNBFr.NKEzjwCIxNJC";int i=sizeof p/2;char *strchr();int putchar(\
);while(*q){i+=strchr(p,*q++)-p;if(i>=(int)sizeof p)i-=sizeof p-1;putchar(p[i]\
);}return 0;}
Dec 21 '06 #39
Dik T. Winter wrote:
In article <87************@blp.benpfaff.orgbl*@cs.stanford.edu writes:
Random832 <ra****@random.yi.orgwrites:
>
Well - if you can't get a better vendor, the best way would be to dothis:

#include <math.h>
//...
float floating_price;
long int price_cents;
//...
price_cents = round(f*100.);
>
For what it's worth, the round function is new in C99.

For this kind of data, (int)ftrunc(f * 100 + 0.5) works just as well.
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
C99 has some fair level of sophistication in rounding.

7.6.3 Rounding
1 The fegetround and fesetround functions provide control of rounding
direction modes.
7.6.3.1 The fegetround function
Synopsis
1 #include <fenv.h>
int fegetround(void);
Description
2 The fegetround function gets the current rounding direction.
Returns
3 The fegetround function returns the value of the rounding direction
macro representing the current rounding direction or a negative value
if there is no such rounding direction macro or the current rounding
direction is not determinable.
7.6.3.2 The fesetround function
Synopsis
1 #include <fenv.h>
int fesetround(int round);
Description
2 The fesetround function establishes the rounding direction
represented by its argument round. If the argument is not equal to the
value of a rounding direction macro, the rounding direction is not
changed.
Returns
3 The fesetround function returns a zero value if and only if the
argument is equal to a rounding direction macro (that is, if and only
if the requested rounding direction was established).
4 EXAMPLE Save, set, and restore the rounding direction. Report an
error and abort if setting the rounding direction fails.

#include <fenv.h>
#include <assert.h>
void f(int round_dir)
{
#pragma STDC FENV_ACCESS ON
int save_round;
int setround_ok;
save_round = fegetround();
setround_ok = fesetround(round_dir);
assert(setround_ok == 0);
/* ... */
fesetround(save_round);
/* ... */
}
7.12.9.6 The round functions
Synopsis
1 #include <math.h>
double round(double x);
float roundf(float x);
long double roundl(long double x);
Description
2 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.
Returns
3 The round functions return the rounded integer value.
7.12.9.7 The lround and llround functions
Synopsis
1 #include <math.h>
long int lround(double x);
long int lroundf(float x);
long int lroundl(long double x);
long long int llround(double x);
long long int llroundf(float x);
long long int llroundl(long double x);
Description
2 The lround and llround functions round their argument to the nearest
integer value, rounding halfway cases away from zero, regardless of the
current rounding direction. If the rounded value is outside the range
of the return type, the numeric result is unspecified. A range error
may occur if the magnitude of x is too large.
Returns
3 The lround and llround functions return the rounded integer value.
7.12.9.8 The trunc functions
Synopsis
1 #include <math.h>
double trunc(double x);
float truncf(float x);
long double truncl(long double x);
Description
2 The trunc functions round their argument to the integer value, in
floating format, nearest to but no larger in magnitude than the
argument.
Returns
3 The trunc functions return the truncated integer value.

On the other hand, I surprised that they did not at least model the
ANSI/ISO SQL rounding function, which is a lot more likely to be what
people need.

Dec 21 '06 #40

Dik T. Winter wrote:
In article <11**********************@i12g2000cwa.googlegroups .com"William Hughes" <wp*******@hotmail.comwrites:
Dilip wrote:
...
THis isn't always possible. The market data vendor I connect to
insists that I read the stock prices as float. So when I reach into
the byte-stream I will have to memcpy sizeof(float) number of bytes.
Since everywhere else the system uses double to hold these prices, I
had no choice but to stuff that retreived float value into a double
causing all these problems.
>
A system that uses floating point variables to store prices should
work correctly if a price is just above or just below the
correct price (e.g for a price of 0.010001 dollars or 0.009998
dollars).
The system you use does not have this property. It needs
to be fixed.

How do you fix a system that follows the C standard?
A possible workaround:
Round to the nearest cent or tenth of a cent (*)
Convert to double
Add a fudge factor of 0.00000001
Now your prices will be subtly wrong, but they will be subtly wrong in
the correct direction. This should work as well as the
(double)(f*1000.0)/1000.0
kludge, and will work for any value.

But after subtraction you can be subtly wrong the wrong way.

Indeed. This is not a fix. This is a kludge that might work with
a broken system.
The suggested kludge does not make the errors any worse
(we expect an error of about 10e-6 times the shareprice)
It just insures that the errors have a consistent sign. The
alternative
of not adding the fudge factor mean that there will still
be some error with unknown sign. The preferred alternatives
of avoiding or rewriting the software do not appear to be
available.
To avoid
all this is done by internally working with integers, longs, long longs,
or whatever integer size you need.
Just remember you may be dealing with the world GDP expressed
in Turkish lira. Or design your system to use floating point and be
tolerant of
small errors In the end you need to find out what the correct answer
is (this is an accounting, not a mathematical or computer question)
data type or structure you use to do this is of lesser importance.

- William Hughes

Dec 21 '06 #41
Keith Thompson <ks***@mib.orgwrites:
CBFalconer <cb********@yahoo.comwrites:
>Dilip wrote:
>>Dik T. Winter wrote:
"Dilip" <rd*****@lycos.comwrites:

(consider 'f' in real life to hold stock prices)

Consider not to use floating point for stock prices. Floating
point inherently carries imprecision (especially if you want to
do decimal calculations), which you do not want with stock prices.

THis isn't always possible. The market data vendor I connect to
insists that I read the stock prices as float. So when I reach
into the byte-stream I will have to memcpy sizeof(float) number of
bytes. Since everywhere else the system uses double to hold these
prices, I had no choice but to stuff that retreived float value
into a double causing all these problems.

This is inherently impossible. While some systems use IEEE float
format, many do not, so you don't even know the bit pattern coming
in. Are you sure you aren't reading and converting a text stream?
If so all you have to do is write a suitable parser for a
particular text format, say dddd.dd, where d is a digit.

It's inherently possible to do this portably.
Sorry, I meant to write that it's inherently *impossible* to do this
portably. (Which might be an overstatement, assuming the input format
is well-defined.)
My guess (and it's only a guess) is that his vendor is providing raw
data of type "float", probably in 32-bit IEEE format in some
particular byte order. If Dilip has already been successful in
storing this incoming data in objects of type "float", then that part
of the problem is solved.

The remaining problem is what to do with the information once he has
it.
--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Dec 21 '06 #42

William Hughes wrote:
Dik T. Winter wrote:
In article <11**********************@i12g2000cwa.googlegroups .com"William Hughes" <wp*******@hotmail.comwrites:
Dilip wrote:
...
THis isn't always possible. The market data vendor I connect to
insists that I read the stock prices as float. So when I reach into
the byte-stream I will have to memcpy sizeof(float) number of bytes.
Since everywhere else the system uses double to hold these prices, I
had no choice but to stuff that retreived float value into a double
causing all these problems.
>
A system that uses floating point variables to store prices should
work correctly if a price is just above or just below the
correct price (e.g for a price of 0.010001 dollars or 0.009998
dollars).
The system you use does not have this property. It needs
to be fixed.
How do you fix a system that follows the C standard?
A possible workaround:
Round to the nearest cent or tenth of a cent (*)
Convert to double
Add a fudge factor of 0.00000001
Now your prices will be subtly wrong, but they will be subtly wrong in
the correct direction. This should work as well as the
(double)(f*1000.0)/1000.0
kludge, and will work for any value.
But after subtraction you can be subtly wrong the wrong way.

Indeed. This is not a fix. This is a kludge that might work with
a broken system.
The suggested kludge does not make the errors any worse
(we expect an error of about 10e-6 times the shareprice)
It just insures that the errors have a consistent sign. The
alternative
of not adding the fudge factor mean that there will still
be some error with unknown sign. The preferred alternatives
of avoiding or rewriting the software do not appear to be
available.
To avoid
all this is done by internally working with integers, longs, long longs,
or whatever integer size you need.

Just remember you may be dealing with the world GDP expressed
in Turkish lira. Or design your system to use floating point and be
tolerant of
small errors In the end you need to find out what the correct answer
is (this is an accounting, not a mathematical or computer question)
data type or structure you use to do this is of lesser importance.
A 64 bit integer will correctly model currency to 18 digits (with
hundredths units that gives 16 digits for the integer part).
In a mythical currency with 1,000,000 units per dollar, that would
leave 10 billion dollar transactions accurate to the penny.

With 6 digits {typically} of precision, float can barely handle a
decent paycheck. Float is one of the most onerous native C data type
that I can imagine for use with currency. Our database tools do all
calcuations in 110 digits of precision (so that things like interest
calculations for the national debt over 100 years would still yield
sensible results).

For instance,
select convert(8624231011335.27 * pow(1+(.05/4),100*4), varchar(256))
returns:
1240889574596181.052486225089780183871526337581526 97346355368797339001098783576983458982091988194949 844928
and {a bit more topically)
select convert(round( 8624231011335.27 * pow(1+(.05/4),100*4),2),
varchar(256))
returns:
1240889574596181.05
which is to say if the US stopped spening more than they take in, and
the national debt accrued at 5% compounding quarterly, then in 100
years the US will owe:
\$1,240,889,574,596,181.05
Which is able tofit in a 64 bit integer, as pennies {barely}
;-)

- William Hughes
Dec 21 '06 #43

dc*****@connx.com wrote:
William Hughes wrote:
Dik T. Winter wrote:
In article <11**********************@i12g2000cwa.googlegroups .com"William Hughes" <wp*******@hotmail.comwrites:
Dilip wrote:
...
THis isn't always possible. The market data vendor I connect to
insists that I read the stock prices as float. So when I reach into
the byte-stream I will have to memcpy sizeof(float) number of bytes.
Since everywhere else the system uses double to hold these prices, I
had no choice but to stuff that retreived float value into a double
causing all these problems.
>
A system that uses floating point variables to store prices should
work correctly if a price is just above or just below the
correct price (e.g for a price of 0.010001 dollars or 0.009998
dollars).
The system you use does not have this property. It needs
to be fixed.
>
How do you fix a system that follows the C standard?
>
A possible workaround:
Round to the nearest cent or tenth of a cent (*)
Convert to double
Add a fudge factor of 0.00000001
Now your prices will be subtly wrong, but they will be subtly wrong in
the correct direction. This should work as well as the
(double)(f*1000.0)/1000.0
kludge, and will work for any value.
>
But after subtraction you can be subtly wrong the wrong way.

Indeed. This is not a fix. This is a kludge that might work with
a broken system.
The suggested kludge does not make the errors any worse
(we expect an error of about 10e-6 times the shareprice)
It just insures that the errors have a consistent sign. The
alternative
of not adding the fudge factor mean that there will still
be some error with unknown sign. The preferred alternatives
of avoiding or rewriting the software do not appear to be
available.
To avoid
all this is done by internally working with integers, longs, long longs,
or whatever integer size you need.
Just remember you may be dealing with the world GDP expressed
in Turkish lira. Or design your system to use floating point and be
tolerant of
small errors In the end you need to find out what the correct answer
is (this is an accounting, not a mathematical or computer question)
data type or structure you use to do this is of lesser importance.

A 64 bit integer will correctly model currency to 18 digits (with
hundredths units that gives 16 digits for the integer part).
In a mythical currency with 1,000,000 units per dollar, that would
leave 10 billion dollar transactions accurate to the penny.

With 6 digits {typically} of precision, float can barely handle a
decent paycheck. Float is one of the most onerous native C data type
that I can imagine for use with currency. Our database tools do all
calcuations in 110 digits of precision (so that things like interest
calculations for the national debt over 100 years would still yield
sensible results).

For instance,
select convert(8624231011335.27 * pow(1+(.05/4),100*4), varchar(256))
returns:
1240889574596181.052486225089780183871526337581526 97346355368797339001098783576983458982091988194949 844928
and {a bit more topically)
select convert(round( 8624231011335.27 * pow(1+(.05/4),100*4),2),
varchar(256))
returns:
1240889574596181.05
which is to say if the US stopped spening more than they take in, and
the national debt accrued at 5% compounding quarterly, then in 100
years the US will owe:
\$1,240,889,574,596,181.05
Which is able tofit in a 64 bit integer, as pennies {barely}
;-)
A floating point version gets the 4 most significant digits correct:

C:\tmp>type f.c
#include <stdio.h>
#include <math.h>
int main(void)
{
float usnd = 8624231011335.27f *
powf(1.f+(.05f/4.f),100.f*4.f);
printf("Current debt {if stabilized} in 100 years = %20.2f\n",
usnd);
return 0;
}

C:\tmp>f
Current debt {if stabilized} in 100 years = 1240912957014016.00

C:\tmp>
- William Hughes
Dec 21 '06 #44

dcor...@connx.com wrote:
dc*****@connx.com wrote:
William Hughes wrote:
Dik T. Winter wrote:
In article <11**********************@i12g2000cwa.googlegroups .com"William Hughes" <wp*******@hotmail.comwrites:
Dilip wrote:
...
THis isn't always possible. The market data vendor I connect to
insists that I read the stock prices as float. So when I reach into
the byte-stream I will have to memcpy sizeof(float) number of bytes.
Since everywhere else the system uses double to hold these prices, I
had no choice but to stuff that retreived float value into a double
causing all these problems.
>
A system that uses floating point variables to store prices should
work correctly if a price is just above or just below the
correct price (e.g for a price of 0.010001 dollars or 0.009998
dollars).
The system you use does not have this property. It needs
to be fixed.

How do you fix a system that follows the C standard?

A possible workaround:
Round to the nearest cent or tenth of a cent (*)
Convert to double
Add a fudge factor of 0.00000001
Now your prices will be subtly wrong, but they will be subtly wrong in
the correct direction. This should work as well as the
(double)(f*1000.0)/1000.0
kludge, and will work for any value.

But after subtraction you can be subtly wrong the wrong way.
>
>
Indeed. This is not a fix. This is a kludge that might work with
a broken system.
The suggested kludge does not make the errors any worse
(we expect an error of about 10e-6 times the shareprice)
It just insures that the errors have a consistent sign. The
alternative
of not adding the fudge factor mean that there will still
be some error with unknown sign. The preferred alternatives
of avoiding or rewriting the software do not appear to be
available.
>
To avoid
all this is done by internally working with integers, longs, long longs,
or whatever integer size you need.
>
Just remember you may be dealing with the world GDP expressed
in Turkish lira. Or design your system to use floating point and be
tolerant of
small errors In the end you need to find out what the correct answer
is (this is an accounting, not a mathematical or computer question)
data type or structure you use to do this is of lesser importance.
A 64 bit integer will correctly model currency to 18 digits (with
hundredths units that gives 16 digits for the integer part).
In a mythical currency with 1,000,000 units per dollar, that would
leave 10 billion dollar transactions accurate to the penny.

With 6 digits {typically} of precision, float can barely handle a
decent paycheck. Float is one of the most onerous native C data type
that I can imagine for use with currency. Our database tools do all
calcuations in 110 digits of precision (so that things like interest
calculations for the national debt over 100 years would still yield
sensible results).

For instance,
select convert(8624231011335.27 * pow(1+(.05/4),100*4), varchar(256))
returns:
1240889574596181.052486225089780183871526337581526 97346355368797339001098783576983458982091988194949 844928
and {a bit more topically)
select convert(round( 8624231011335.27 * pow(1+(.05/4),100*4),2),
varchar(256))
returns:
1240889574596181.05
which is to say if the US stopped spening more than they take in, and
the national debt accrued at 5% compounding quarterly, then in 100
years the US will owe:
\$1,240,889,574,596,181.05
Which is able tofit in a 64 bit integer, as pennies {barely}
;-)

A floating point version gets the 4 most significant digits correct:

C:\tmp>type f.c
#include <stdio.h>
#include <math.h>
int main(void)
{
float usnd = 8624231011335.27f *
powf(1.f+(.05f/4.f),100.f*4.f);
printf("Current debt {if stabilized} in 100 years = %20.2f\n",
usnd);
return 0;
}

C:\tmp>f
Current debt {if stabilized} in 100 years = 1240912957014016.00

C:\tmp>
Here is a double version:

C:\tmp>type d.c
#include <stdio.h>
#include <math.h>
int main(void)
{
double usnd = 8624231011335.27 * pow(1.+(.05/4.),100.*4.);
printf("Current debt {if stabilized} in 100 years = %20.2f\n",
usnd);
return 0;
}

C:\tmp>d
Current debt {if stabilized} in 100 years = 1240889574596159.20

C:\tmp>

which only misses by \$21.85 (not at all surprising -- 15th digit is
dodgy)
- William Hughes
Dec 21 '06 #45

dc*****@connx.com wrote:
William Hughes wrote:
Dik T. Winter wrote:
In article <11**********************@i12g2000cwa.googlegroups .com"William Hughes" <wp*******@hotmail.comwrites:
Dilip wrote:
...
THis isn't always possible. The market data vendor I connect to
insists that I read the stock prices as float. So when I reach into
the byte-stream I will have to memcpy sizeof(float) number of bytes.
Since everywhere else the system uses double to hold these prices, I
had no choice but to stuff that retreived float value into a double
causing all these problems.
>
A system that uses floating point variables to store prices should
work correctly if a price is just above or just below the
correct price (e.g for a price of 0.010001 dollars or 0.009998
dollars).
The system you use does not have this property. It needs
to be fixed.
>
How do you fix a system that follows the C standard?
>
A possible workaround:
Round to the nearest cent or tenth of a cent (*)
Convert to double
Add a fudge factor of 0.00000001
Now your prices will be subtly wrong, but they will be subtly wrong in
the correct direction. This should work as well as the
(double)(f*1000.0)/1000.0
kludge, and will work for any value.
>
But after subtraction you can be subtly wrong the wrong way.

Indeed. This is not a fix. This is a kludge that might work with
a broken system.
The suggested kludge does not make the errors any worse
(we expect an error of about 10e-6 times the shareprice)
It just insures that the errors have a consistent sign. The
alternative
of not adding the fudge factor mean that there will still
be some error with unknown sign. The preferred alternatives
of avoiding or rewriting the software do not appear to be
available.
To avoid
all this is done by internally working with integers, longs, long longs,
or whatever integer size you need.
Just remember you may be dealing with the world GDP expressed
in Turkish lira. Or design your system to use floating point and be
tolerant of
small errors In the end you need to find out what the correct answer
is (this is an accounting, not a mathematical or computer question)
data type or structure you use to do this is of lesser importance.

A 64 bit integer will correctly model currency to 18 digits (with
hundredths units that gives 16 digits for the integer part).
In a mythical currency with 1,000,000 units per dollar,
Nothing mythical here.

1 US dollar = 1374865.7 Turkish Lira
that would
leave 10 billion dollar transactions accurate to the penny.
So don't use 64 bit integers to calculate the world GDP in
Turkish Lira unless your system is tolerant of small errors.
>
With 6 digits {typically} of precision, float can barely handle a
decent paycheck. Float is one of the most onerous native C data type
that I can imagine for use with currency.
Both char and short are (probably) worse.
Double on the other hand is fine for many uses.
float has many uses, currency calculations is not among them.

Our database tools do all
calcuations in 110 digits of precision (so that things like interest
calculations for the national debt over 100 years would still yield
sensible results).
As pointed out: Find out what the correct answer is, then design
your system. It may be that no native type is sufficient.

>
For instance,
select convert(8624231011335.27 * pow(1+(.05/4),100*4), varchar(256))
returns:
1240889574596181.052486225089780183871526337581526 97346355368797339001098783576983458982091988194949 844928

Looks to me like you are using high precision floating point arithmetic
(rather than arbitrary precision integer arithmetic), then rounding to
get your final answer. Nothing wrong with this strategy, but it is
hardly an argument in favour of integer arithmetic.

- William
Hughes

Dec 21 '06 #46
In article <87************@blp.benpfaff.orgbl*@cs.stanford.edu writes:
"Dik T. Winter" <Di********@cwi.nlwrites:
In article <87************@blp.benpfaff.orgbl*@cs.stanford.edu writes:
Random832 <ra****@random.yi.orgwrites:
price_cents = round(f*100.);
For what it's worth, the round function is new in C99.
For this kind of data, (int)ftrunc(f * 100 + 0.5) works just as well.

I can't find any mention of ftrunc, in C99 or even in SUSv3.
Yup. I meant "floor". (That happens when you are using a large
variety of environments.)
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
Dec 21 '06 #47
dc*****@connx.com wrote:
>
CBFalconer wrote:
>Ben Pfaff wrote:
Random832 <ra****@random.yi.orgwrites:

Well - if you can't get a better vendor, the best way would be
to do this:

#include <math.h>
//...
float floating_price;
long int price_cents;
//...
price_cents = round(f*100.);

For what it's worth, the round function is new in C99.

and unnecessary.

price_cents = 100 * floating_price + 0.5;

Almost:
>>From the C-FAQ:
14.6: How do I round numbers?

A: The simplest and most straightforward way is with code like

(int)(x + 0.5)

This technique won't work properly for negative numbers,
though (for which you could use something like
(int)(x < 0 ? x - 0.5 : x + 0.5)).

Also, most banking institutions will prefer banker's rounding to simple
rounding.
This is for converting floats that are precise to one cent to ints
_once_, not how to round stuff for ongoing calculations. I.e, you're not
trying to figure out which way to round 12.945 in, you're trying to
figure out whether when the data says 12.94999980926513671875, if that's
12.94 or 12.95.
Dec 21 '06 #48
dc*****@connx.com wrote:
In a mythical currency with 1,000,000 units per dollar,
The Turkish Lira is not mythical, though it is obsolete.
Dec 21 '06 #49
In article <11**********************@n67g2000cwd.googlegroups .com"William Hughes" <wp*******@hotmail.comwrites:
Dik T. Winter wrote:
....
But after subtraction you can be subtly wrong the wrong way.

Indeed. This is not a fix. This is a kludge that might work with
a broken system.
What of the system is broken?
To avoid
all this is done by internally working with integers, longs, long longs,
or whatever integer size you need.

Just remember you may be dealing with the world GDP expressed
in Turkish lira. Or design your system to use floating point and be
tolerant of small errors.
The current Turkish Lira would do pretty well. The previous one would
be bad, especially if you want to keep up to figures until the nearest
Kuru. But when somebody complains that there is a difference of one
cent in the result, that only means that the calculations are done
using the wrong data type. If you expect that anybody in Turkey before
the introduction of the new Lira bothered about anything less than
25000 Lira, you are wrong. (I still remember the 10,000,000 Lira tip
for the waiter after a small lunch for four.)

No financial program can cope with hyperinflation when it does not
regularly adjust to the inputs. You should know the smallest amount
that legally can be distinguished and base your calculations on that;
using integers. Picking the Turkish Lira is a red herring. You
could equally well have picked the Hungarian Pengo from just after
the war. If I have it right, at some moment they had notes of
1,000,000,000,000,000,000,000,000 Pengo. Or the Zimbabwian dollar
notes that have printed on them an ultimate date of validity (which
is not more than six months after introduction).

The whole point is that in most financial transactions it is precisely
defined how fractions of something should be rounded. Any attempt to
be slightly imprecise (using floating point) will fail at some point
or another.
In the end you need to find out what the correct answer
is (this is an accounting, not a mathematical or computer question)
data type or structure you use to do this is of lesser importance.
But floating point does not help at all.
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
Dec 21 '06 #50

### This discussion thread is closed

Replies have been disabled for this discussion.