long double imprecisions

53 views
Skip to first unread message

Janis Taranda

unread,
Apr 30, 2015, 5:01:05 AM4/30/15
to std-dis...@isocpp.org
When I run the code:

    long double xxx = 0.0001000001000001;

    std
::cout << "right: " << 0.0001000001000001e21 << std::endl;
    std
::cout << "wrong: " << xxx*1e21 << std::endl;
    std
::cout << "wrong: " << xxx*10000000000000000*100000 << std::endl;


   
for(int yyy = 0; yyy < 21; yyy++){
        xxx
= xxx * 10;
   
}

    std
::cout << "wrong: " << xxx << std::endl;


I get:

right: 100000100000100000

wrong: 100000100000100002.3359375

wrong: 100000100000100002.328125

wrong: 100000100000100002.328125


Where I point out right & wrong by common sense and help of math&calculator. Can someone explain WHY? And how can I avoid this?


Thanks, Janis



David Krauss

unread,
Apr 30, 2015, 5:18:04 AM4/30/15
to std-dis...@isocpp.org
On 2015–04–30, at 5:01 PM, Janis Taranda <janis....@gmail.com> wrote:

Where I point out right & wrong by common sense and help of math&calculator. Can someone explain WHY?

The literal 0.0001000001000001 has type double. You need 0.0001000001000001L to get a literal with long double precision.

Binary floating-point arithmetic represents fractions by a sum of powers of two. In the first output line, there is no fractional part to the error because you have named an integer. Any rounding of integer values will always result in other integers, but rounding of fractional values may result in “added” decimal places. You observe these digits in the long double type, even after multiplication brings them into integer decimal places, because it has more precision than double.

In any case, the proportion of the error for normalized values of any floating-point type T is at most std::numeric_limits<T>::epsilon.

And how can I avoid this?

1. Use the L suffix on fractional literals.
2. Mind the least-significant bits and don’t use them when they are within the margin of error.
3. Use decimal floating-point math instead of binary. The C++ standard for decimal math is ISO TR 24733. I don’t know its implementation status, though.

Janis Taranda

unread,
Apr 30, 2015, 5:55:39 AM4/30/15
to std-dis...@isocpp.org
Hi,

first of all thanks for your reply. I do understand your explanation, but I do not see a real solution. For example:
   
long double xxx = 1000001000001L / 1e17L;


   
for(int yyy = 0; yyy < 21; yyy++){

        xxx
= xxx * 10L;

   
}

    std
::cout << "wrong: " << xxx << std::endl;

It's still calculated wrong. For more multiply by 10, it leads to larger error.

wrong: 10000010000009999.9990234375

The only solution I see, is to round it, when ^17 is reached. But in my code, it's very hard to get those point. This is just a snippet from a larger project. :(


Best, Janis



The man problem is, when, for example

Janis Taranda

unread,
Apr 30, 2015, 6:04:06 AM4/30/15
to std-dis...@isocpp.org
Or convert xxx to as string, add + 'e21', and than back to double. Then I get a clean number, but feels stupid to go such long way :(

Thiago Macieira

unread,
Apr 30, 2015, 10:38:06 AM4/30/15
to std-dis...@isocpp.org
On Thursday 30 April 2015 02:55:39 Janis Taranda wrote:
> Hi,
>
> first of all thanks for your reply. I do understand your explanation, but I
> do not see a real solution. For example:
>
> long double xxx = 1000001000001L / 1e17L;
>
> for(int yyy = 0; yyy < 21; yyy++){
> xxx = xxx * 10L;
> }
>
> std::cout << "wrong: " << xxx << std::endl;
>
> It's still calculated wrong. For more multiply by 10, it leads to larger
> error.
>
> wrong: 10000010000009999.9990234375
>
> The only solution I see, is to round it, when ^17 is reached. But in my
> code, it's very hard to get those point. This is just a snippet from a
> larger project. :(

The error was there before you started.

The value of 1000001000001L / 1e17L or of 0.0001000001000001L isn't precise.
The standard IEEEE 754 extended-precision floating point has 18 digits of
decimal precision, which means the 18th digit is not actually very precise (it
has less than 0.5 error only).

Look at the digit count:
10000010000009999.9990234375
1234567890123456789012345678

The error for this number in the 18th digit is 1 - 0.990234375 or 0.97%, which
is pretty good.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358

David Krauss

unread,
Apr 30, 2015, 10:38:40 AM4/30/15
to std-dis...@isocpp.org

On 2015–04–30, at 5:55 PM, Janis Taranda <janis....@gmail.com> wrote:

wrong: 10000010000009999.9990234375

I already explained how integers are special, and my suggestions #2 and #3 are relevant to this new, literally smaller, issue.

A fourth solution could be to increase the scale of all the exact values so they turn out to be integers in the first place. It really depends on what you’re doing, which isn’t obvious.

The only solution I see, is to round it, when ^17 is reached. But in my code, it's very hard to get those point. This is just a snippet from a larger project. :(

Numerics is hard.

If you’re looking for an exact integer, and your margin of error includes exactly one integer, then rounding might be a good solution.

This mailing list is for discussion of the C++ standard. If you need help with numerics, try StackOverflow.
Reply all
Reply to author
Forward
0 new messages