Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

decimal

41 views
Skip to first unread message

Christopher Pisz

unread,
Jun 8, 2015, 12:49:47 PM6/8/15
to

I've run into the problem where I compare two different values of type
double and they are different by a tiny bit because of the way floating
point numbers are represented.

With all the new standard stuff, is there any representation that
guarantees to be exact to a certain precision?

I could write my own code and call it for comparisons with some
precision argument, that's what I did in 2008...


--
I have chosen to troll filter/ignore all subthreads containing the
words: "Rick C. Hodgins", "Flibble", and "Islam"
So, I won't be able to see or respond to any such messages
---

Victor Bazarov

unread,
Jun 8, 2015, 1:32:07 PM6/8/15
to
On 6/8/2015 12:49 PM, Christopher Pisz wrote:
> I've run into the problem where I compare two different values of type
> double and they are different by a tiny bit because of the way floating
> point numbers are represented.

Are you stepping again on the same rake? Never compare doubles for
equality unless they are both assigned from the same object originally.

> With all the new standard stuff, is there any representation that
> guarantees to be exact to a certain precision?

Of course. Integer, long, bool, char. There is rational, too. Oh, did
you mean 'float' or 'double' or 'long double'? Then, no.

> I could write my own code and call it for comparisons with some
> precision argument, that's what I did in 2008...

It's time for you to re-read the well-known article:
http://www.google.com/search?q=%22What+Every+Computer+Scientist+Should+Know+About+Floating-Point+Arithmetic%22

V
--
I do not respond to top-posted replies, please don't ask

Marcel Mueller

unread,
Jun 8, 2015, 1:45:07 PM6/8/15
to
On 08.06.15 18.49, Christopher Pisz wrote:
> I've run into the problem where I compare two different values of type
> double and they are different by a tiny bit because of the way floating
> point numbers are represented.
>
> With all the new standard stuff, is there any representation that
> guarantees to be exact to a certain precision?

Any floating point representation is exact as long as the mantissa is
long enough. double as well as float as well as a decimal float class.
But rational numbers must not be periodic in the specific number system.
I.e. nothing but the prime factor 2 and 5 (decimal float only) in the
denominator.

And most algorithms that compare floating point numbers to anything else
but singular values are broken by design.

> I could write my own code and call it for comparisons with some
> precision argument, that's what I did in 2008...

There are many big decimal or big float implementations around. Some of
them keep track of the accuracy. But they are not part of the standard
library.
And none of them replaces the know how about numeric. Many algorithms
can be tweaked for one or more orders of magnitude more accuracy simply
by carefully dealing with the residuals.


Marcel

Mr Flibble

unread,
Jun 8, 2015, 1:49:18 PM6/8/15
to
On 08/06/2015 18:31, Victor Bazarov wrote:
> On 6/8/2015 12:49 PM, Christopher Pisz wrote:
>> I've run into the problem where I compare two different values of type
>> double and they are different by a tiny bit because of the way floating
>> point numbers are represented.
>
> Are you stepping again on the same rake? Never compare doubles for
> equality unless they are both assigned from the same object originally.
>
>> With all the new standard stuff, is there any representation that
>> guarantees to be exact to a certain precision?
>
> Of course. Integer, long, bool, char. There is rational, too. Oh, did
> you mean 'float' or 'double' or 'long double'? Then, no.

In modern C++ we should avoid using 'int', 'short' and 'long' as they
are not portable or safe. Instead use the typedefs from <cstdint>.

/Flibble
Message has been deleted

Richard

unread,
Jun 8, 2015, 2:04:33 PM6/8/15
to
[Please do not mail me a copy of your followup]

Victor Bazarov <v.ba...@comcast.invalid> spake the secret code
<ml4jg3$ber$1...@dont-email.me> thusly:
Beat me to it :)
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>

Victor Bazarov

unread,
Jun 8, 2015, 2:09:09 PM6/8/15
to
On 6/8/2015 1:49 PM, Stefan Ram wrote:
> Victor Bazarov <v.ba...@comcast.invalid> writes:
>> Never compare doubles for
>> equality unless they are both assigned from the same object originally.
>
> What about the following example?
>
> void print_reciprocal( double const x )
> { if( x )::std::cout << 1/x;
> else ::std::cout << "undefined";
> ::std::cout << '\n'; }

What about that example? Do you know that NaN can cause a problem when
used in an expression? And it compares unequal to itself, even. The
behaviour of that code is only defined for a subset of 'double' values,
IOW, in general it's undefined.

>
>> Of course. Integer, long, bool, char. There is rational, too. Oh, did
>> you mean 'float' or 'double' or 'long double'? Then, no.
>
> Or possible he wants to download
> a decimal floating point library.

I am not sure I understand that statement. Downloading "a decimal
floating point library" is well-defined, but what's it got to do with
the problem of imprecision of doubles?
Message has been deleted

Christopher Pisz

unread,
Jun 8, 2015, 3:14:29 PM6/8/15
to
> The imprecision is often visible for the layman or in
> financial application only when it differs from everyday
> imprecision.
>
> That is, with fractions numerals with only a few digits
> after the dot. A most famous example being
>
> #include <iostream>
> #include <ostream>
>
> int main(){ ::std::cout << 0.1 + 0.2 - 0.3 << '\n'; }
>
> 5.55112e-017
>
> This kind of imprecision can be defeated with /decimal/
> floating point arithmetics.
>
> mysql> SELECT 0.1 + 0.2 - 0.3;
> +-----------------+
> | 0.1 + 0.2 - 0.3 |
> +-----------------+
> | 0.0 |
> +-----------------+
> 1 row in set (0.00 sec)
>


Pretty much and example of my problem now.
1) Getting data from a database that represents money
2) Comes into C++ as a string
3) Convert it double to do calculations
4) Convert it back to a string
5) Put it in the database again

So, $2.13 an hour turns into 2.129999999999999....I have a feeling noone
wants that printed on their paycheck.

I don't want to just round, because I'll have other cases where rounding
might not be the best choice.

What is a widely used C++ library for this kind of thing?
I see boost::multiprecision, but I'm not really clear on the licenses
bit of the documentation there.
Message has been deleted

Victor Bazarov

unread,
Jun 8, 2015, 3:39:50 PM6/8/15
to
> The imprecision is often visible for the layman or in
> financial application only when it differs from everyday
> imprecision.
>
> That is, with fractions numerals with only a few digits
> after the dot. A most famous example being
>
> #include <iostream>
> #include <ostream>
>
> int main(){ ::std::cout << 0.1 + 0.2 - 0.3 << '\n'; }
>
> 5.55112e-017
>
> This kind of imprecision can be defeated with /decimal/
> floating point arithmetics.

Ah... You were talking of a very specific case of imprecision and that
just happens to be the OP's problem. That's lucky. Of course, some say
that it's better to be lucky than good. I imagine, however, that such a
library might not work very well with pounds/shillings/pence since the
relationship is not based on powers of 10 (not talking about the modern
system).

> mysql> SELECT 0.1 + 0.2 - 0.3;
> +-----------------+
> | 0.1 + 0.2 - 0.3 |
> +-----------------+
> | 0.0 |
> +-----------------+
> 1 row in set (0.00 sec)

Generally speaking, the rounding of the output can take care of that as
well, even on C++ side...

Scott Lurndal

unread,
Jun 8, 2015, 3:39:54 PM6/8/15
to
Christopher Pisz <nos...@notanaddress.com> writes:
>
>I've run into the problem where I compare two different values of type
>double and they are different by a tiny bit because of the way floating
>point numbers are represented.
>
>With all the new standard stuff, is there any representation that
>guarantees to be exact to a certain precision?
>
>I could write my own code and call it for comparisons with some
>precision argument, that's what I did in 2008...

In the old days, we'd just use integers and scale them as
necessary.

e.g. denominate in mills and divide by 1000 to get dollars.

Jens Thoms Toerring

unread,
Jun 8, 2015, 4:40:00 PM6/8/15
to
Do you actually need doubles for the calculations or would
doing the calculations on cents instead of Dollars or Euros
also do? In the latter case simply don't use floating point
values but multiply everything you get from the data base by
100 and stuff it after proper rounding (to avoid 212.9999999
become 212 via a simple assignment instead of 213) into some
integer type. Unless this is about huge amounts of money (more
than 20 Million Dollar/Euro) a long integer will do quire
nicely. Or go for a int64 if you've got to deal with the
worlds combined GNPs;-)

> 4) Convert it back to a string
> 5) Put it in the database again

When you write it back into the data base divide by 100 and
the strings send to the data base should be fine (whatever
format you use, given that it allows for at least to digits
after the decimal point) should round correctly.

> So, $2.13 an hour turns into 2.129999999999999....I have a feeling noone
> wants that printed on their paycheck.

The problem with "I want a precision of two digits after the
decimal point" is rather simple: the computer works on binary
numbers. And most numbers that have just two significant
fractional digits in a decimal reprentation are infinite
fractions in binary (e.g. something seemingly simple as 0.1)
- and vice versa. This, obviously, becomes a problem when
you have only a finite amount of bits to store such a number
in. So there's no simple solution to the "I want two decimal
digits" problem.

> I don't want to just round, because I'll have other cases where rounding
> might not be the best choice.

If you can't switch to using cents, and thus integer types
and arithmetic, you have to very carefully analyze what you
actually need and write your code to do exactly what you want
it to do. So what's the exact scenario where rounding won't
do?

> What is a widely used C++ library for this kind of thing?
> I see boost::multiprecision, but I'm not really clear on the
> licenses bit of the documentation there.

As fas as I can see the Boost license is rather liberal, allowing
you to do whatever you want with their code (even including it
into a closed source program and selling it). The more pressing
question is if "multiprecision" is of any use in this case, since
also a "multiprecision" value can't help a lot if your require-
ments call for an infinite amount of memory for storing just a
single number;-)
Regards, Jens
--
\ Jens Thoms Toerring ___ j...@toerring.de
\__________________________ http://toerring.de

Robert Wessel

unread,
Jun 8, 2015, 6:24:17 PM6/8/15
to
On 8 Jun 2015 20:39:46 GMT, j...@toerring.de (Jens Thoms Toerring)
wrote:
The problem is not so much the actual values (where 64-bit ints will
adequately deal with almost any amount of currency you might
encounter), rather the intermediate results. Consider multiplying a
large number of pennies by a percentage expressed as five decimal
digits (.01..999.99%). If you're working with 32 bit ints, that will
overflow at about $214. 64-bit ints go a little farther, but you
still overflow with something as simple as multiplying $10M by a
couple of percentages (assuming you aren't, as is true in some cases,
allowed to round between the two multiplications), or some $922B for a
single percentage (which is barely adequate to handle Exxon's annual
revenue, to say nothing of, say, Mexico's GDP), so a fair bit of care
is still required. A 128-bit intermediate type goes a long way to
making currency calculations straight-forward.

Christopher Pisz

unread,
Jun 8, 2015, 6:42:07 PM6/8/15
to
I thought that was what the boost multiprecision library took care of.
It was to use base 10. But see below, my demo code doesn't make sense to me.

I understand what 0.1f ends up looking like in binary, but I don't
understand how to get around the problem.

>> I don't want to just round, because I'll have other cases where rounding
>> might not be the best choice.
>
> If you can't switch to using cents, and thus integer types
> and arithmetic, you have to very carefully analyze what you
> actually need and write your code to do exactly what you want
> it to do. So what's the exact scenario where rounding won't
> do?

Well, rounding will probably do in most scenarios I can think of, even
though I'd have to round to a different decimal place depending what I
am dealing with. Money - 2 digits, Time clock punches - maybe 4,
performance timer - 8... something like that.

I was really hoping there was just some C++ equivalent to the decimal
type they have in other languages.

I read the other's posts about existance of libraries. I thought the
boost multiprecision was one such library.

>> What is a widely used C++ library for this kind of thing?
>> I see boost::multiprecision, but I'm not really clear on the
>> licenses bit of the documentation there.
>
> As fas as I can see the Boost license is rather liberal, allowing
> you to do whatever you want with their code (even including it
> into a closed source program and selling it). The more pressing
> question is if "multiprecision" is of any use in this case, since
> also a "multiprecision" value can't help a lot if your require-
> ments call for an infinite amount of memory for storing just a
> single number;-)
> Regards, Jens
>

I tried the following demo code and the output makes no sense to me:

#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/math/special_functions/gamma.hpp>
#include <iostream>

int main()
{
typedef boost::multiprecision::cpp_dec_float_50 Decimal;

Decimal numberBoost = 2.13f;
float numberFloat = 2.13f;
double numberDouble = 2.13;
std::string numberAsText("2.13");

std::cout << "The float number is: " << std::fixed <<
std::setprecision(12) << numberFloat << std::endl;
std::cout << "The double number is: " << std::fixed <<
std::setprecision(12) << numberDouble << std::endl;
std::cout << "The boost number is: " << std::fixed <<
std::setprecision(12) << numberBoost << std::endl;

std::istringstream converter(numberAsText);
converter >> numberDouble;

std::cout << "The number converted from string and back is " <<
numberDouble << std::endl;


return 0;
}


Output:
The float number is: 2.130000114441
The double number is: 2.130000000000
The boost number is: 2.130000114441
The number converted from string and back is 2.130000000000

The double seemed to print to console as the original number, while I
expected it wouldn't. The debugger say's it is holding 2.12999999

The boost number did not print to console matching the original number.

The float printed with a error in positive contrary to what the debugger
told me about the double, which had error in the negative.

I read over the link Victor posted, but I am honestly pretty lost now.
I thought I understood what was going on, but evidently I don't.

Louis Krupp

unread,
Jun 9, 2015, 2:46:55 AM6/9/15
to
On Mon, 08 Jun 2015 17:41:53 -0500, Christopher Pisz
<nos...@notanaddress.com> wrote:

<snip>
> typedef boost::multiprecision::cpp_dec_float_50 Decimal;
>
> Decimal numberBoost = 2.13f;
> float numberFloat = 2.13f;
> double numberDouble = 2.13;
<snip>
>Output:
>The float number is: 2.130000114441
>The double number is: 2.130000000000
>The boost number is: 2.130000114441
>The number converted from string and back is 2.130000000000
>
>The double seemed to print to console as the original number, while I
>expected it wouldn't. The debugger say's it is holding 2.12999999
>
>The boost number did not print to console matching the original number.
>
>The float printed with a error in positive contrary to what the debugger
>told me about the double, which had error in the negative.

My guess is that at least part of your problem is with "2.13f". The
"f" tells the compiler to store the constant as single-precision
floating point, which means that you lose whatever added precision you
might have had if you'd said "2.13d" or if you'd simply said "2.13",
as you did when you initialized numberDouble.

Remember that the compiler doesn't know and doesn't care that you're
going to be using the constant to initialize something of type double
or of type numberBoost.

(What happens if you initialize numberDouble with 2.13f?)

Keep in mind, too, that some Intel CPUs use 80-bit floating point
registers, and you *might* be getting more precision than you
expected. This has been a source of confusion for years; if a value
is written to memory as a 64-bit double-precision floating point value
and is then read from memory, you get one thing, and if it's used
straight from an 80-bit register, you get something slightly
different.

Louis

Öö Tiib

unread,
Jun 9, 2015, 7:21:50 AM6/9/15
to
Your "original number" was C++ float constant value '2.13f'.
2.13f is *impossible* to represent as IEEE 754 float exactly so compiler has to
take 2.13000011444091796875f that happens to be closest to 2.13 that float
*can* represent. So that value *is* in your boost number. Precision of 50
decimal places does not help if you fill it with garbage.

> The float printed with a error in positive contrary to what the debugger
> told me about the double, which had error in the negative.

You initialized the values with different constants ('2.13f' and '2.13'). If you
use same constant for double then you get same output with it as well:

#include <iostream>
#include <iomanip>

int main()
{
double numberDouble = 2.13f;

std::cout << "The double number is: " << std::fixed
<< std::setprecision(25) << numberDouble << std::endl;

return 0;
}

Output:
The double number is: 2.1300001144409179687500000

Note that this outcome is not regulated by C++ standard but by
IEEE Standard for Floating-Point Arithmetic (IEEE 754). C++ standard
keeps being rather quiet about floating point arithmetic.

> I read over the link Victor posted, but I am honestly pretty lost now.
> I thought I understood what was going on, but evidently I don't.

The paper is discussing exactly the issue that you have above and
several other issues that you will very likely face soon enough.
It might take more than few hours to read and to understand the paper.


Drew Lawson

unread,
Jun 9, 2015, 11:39:01 AM6/9/15
to
In article <ml4pfp$4ne$1...@dont-email.me>
Christopher Pisz <nos...@notanaddress.com> writes:

>Pretty much and example of my problem now.
>1) Getting data from a database that represents money
>2) Comes into C++ as a string
>3) Convert it double to do calculations

There is your problem. Never use floating point for money.

In addition to the practical problems you've seen, it is a conceptual
error. Monetary systems have discrete minimum values (frequently
a mil) and rules about how they are treated.

This is the sort of problem that is recreated every time programmers
try to replace centuries of accounting practices.

--
|Drew Lawson | Of all the things I've lost |
| | I miss my mind the most |
0 new messages