converting float to double

D

dcorbit

Dik said:
For this kind of data, (int)ftrunc(f * 100 + 0.5) works just as well.

C99 has some fair level of sophistication in rounding.

ISO/IEC 9899:1999 (E) ©ISO/IEC
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.
 
W

William Hughes

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


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)
and design your system to give the correct answer. What internal
data type or structure you use to do this is of lesser importance.

- William Hughes
 
K

Keith Thompson

Keith Thompson said:
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.)
 
D

dcorbit

William said:
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.


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)
and design your system to give the correct answer. What internal
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.05248622508978018387152633758152697346355368797339001098783576983458982091988194949844928
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}
;-)
 
D

dcorbit

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.05248622508978018387152633758152697346355368797339001098783576983458982091988194949844928
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>
 
D

dcorbit

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)
 
W

William Hughes

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.05248622508978018387152633758152697346355368797339001098783576983458982091988194949844928


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
 
D

Dik T. Winter

>
> 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.)
 
R

Random832

2006-12-20 <[email protected]>,
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.
 
D

Dik T. Winter

> Dik T. Winter wrote: ....
>
> Indeed. This is not a fix. This is a kludge that might work with
> a broken system.

What of the system is broken?
>
> 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)
> and design your system to give the correct answer. What internal
> data type or structure you use to do this is of lesser importance.

But floating point does not help at all.
 
D

Dik T. Winter

>
> Nothing mythical here.
>
> 1 US dollar = 1374865.7 Turkish Lira

You are out of date. 1 US dollar = 1.4283 Turkish Lira.
>
> So don't use 64 bit integers to calculate the world GDP in
> Turkish Lira unless your system is tolerant of small errors.

You still fail to see how the financial world is looking at it.
They require exactness to some predefined unit. And if some
system requires precision to the cent, do the calculations to
the cent.
 
C

CBFalconer

William said:
Dik T. Winter wrote:
.... snip ...

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) and design your system to give the correct
answer. What internal data type or structure you use to do this
is of lesser importance.

Mumble years ago I was rewriting a payroll program (in Cobol) for a
municipality. The original had done all sorts of multiple rounding
operations, giving minor erroneous results, and I corrected this as
a matter of course. The net result was a penny or two difference
in actual paychecks, but summaries were correct and the books
balanced.

You wouldn't believe the screams from the payees. Obviously they
were in the habit of checking things themselves, rounding at every
opportunity. Also they obviously didn't trust "the computer" worth
a damn. To quiet the insurrection I had to put back the faulty
calculations.
 
W

William Hughes

Dik said:
You are out of date. 1 US dollar = 1.4283 Turkish Lira.

No 1 US dollar = 1.4283 New Turkish Lira.

Yes, the convesion contant came from a table that was
a few years old. That hardly makes the Turkish Lira
mythical.
- William Hughes
 
E

Ernie Wright

CBFalconer said:
which won't matter in the case posted by the OP. He started with a
constant of the form 89.13, i.e. with exactly 2 decimal places.

I infer that he started with a 32-bit IEEE float, which can only have
exactly two decimal places if they happen to be ".25", ".50" or ".75".

If he were really starting with a constant in the code, he wouldn't
need a rounding formula. He could just erase the decimal point.
The above will capture the original value.

Which will only help if the OP is planning to rewrite his entire
application to use integers instead of doubles, an option he appears not
to be entertaining.

The problem described by the OP is a truncation happening at a point in
his code that he hasn't shown us. It's very possible that replacing the
truncation with some form of rounding at that point would solve his
problem, but not knowing the exact nature of the problem, we don't know
what kind of rounding he might need.

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

CBFalconer

William said:
No 1 US dollar = 1.4283 New Turkish Lira.

Yes, the convesion contant came from a table that was a few years
old. That hardly makes the Turkish Lira mythical.

Reminds me of the time I was in Paris shortly after they converted
to the New Franc. I was playing bridge for stakes in a local
bridge club, and carefully checked that the stake was in old
Francs. Otherwise it would have been beyond my means to risk.
 
W

William Hughes

Ernie said:
I infer that he started with a 32-bit IEEE float, which can only have
exactly two decimal places if they happen to be ".25", ".50" or ".75".

No, the "two decimal places" refers to the desired value,
a value known by the supplier of the stock prices.
What he has is a float that approximates the desired value.
As you note, this will rarely be exact.
If he were really starting with a constant in the code, he wouldn't
need a rounding formula. He could just erase the decimal point.

No, he is starting with a float provided by a vendor which
approximates a two decimal place value.
Which will only help if the OP is planning to rewrite his entire
application to use integers instead of doubles, an option he appears not
to be entertaining.

No. Rounding, then converting to double, does not produce the
same double value as a direct conversion to double. It is conjectured
(partially based on knowing another kludge succeeded) that this
will be more successful with the broken application.
The problem described by the OP is a truncation happening at a point in
his code that he hasn't shown us. It's very possible that replacing the
truncation with some form of rounding at that point would solve his
problem, but not knowing the exact nature of the problem, we don't know
what kind of rounding he might need.

Fixing the root of the problem appears to be off the table. Hence
the suggested kludge. The rounding used in the kludge is not
financial rounding.

- William Hughes
 
M

Mark McIntyre

A 64 bit integer will correctly model currency to 18 digits (with
hundredths units that gives 16 digits for the integer part).

Thats only 50Bn Euros, converted into Vietnamese Dong. Peanuts...
In a mythical currency with 1,000,000 units per dollar, that would
leave 10 billion dollar transactions accurate to the penny.

10Bn dollars is not a large amount. Some LCH Repoclear members will be
settling transactions with LCH.Clearnet for around a trillion GBP each
day.
--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 
M

Mark McIntyre

You are out of date. 1 US dollar = 1.4283 Turkish Lira.

Old vs new Lira, both still in use.
Anyway, vietnamese dong are a better example...
You still fail to see how the financial world is looking at it.
They require exactness to some predefined unit. And if some
system requires precision to the cent, do the calculations to
the cent.

What makes it interesting is that the "predefined unit" is variable
by currency, market and calculation.
--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 
R

Richard Tobin

Mark McIntyre said:
10Bn dollars is not a large amount. Some LCH Repoclear members will be
settling transactions with LCH.Clearnet for around a trillion GBP each
day.

10 billion dollars *is* a large amount. The existence of larger
amounts is not relevant, assuming that's what your last sentence
implies (it contains too many names I've never heard before to be
sure).

-- Richard
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,019
Latest member
RoxannaSta

Latest Threads

Top