Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.misc > #2816

Re: BASIC is 50

From bde@besplex.bde.org (Bruce Evans)
Newsgroups comp.misc
Subject Re: BASIC is 50
Date 2014-05-14 16:13 +0000
Organization A noiseless patient Spider
Message-ID <ll04np$bp1$1@dont-email.me> (permalink)
References <ljqg3o$8dh$2@dont-email.me> <JJtcv.95786$Sp6.12404@fx15.am4> <lkvgvv$19v$1@dont-email.me> <jUIcv.37987$ub6.35971@fx32.am4>

Show all headers | View raw


In article <jUIcv.37987$ub6.35971@fx32.am4>, BartC <bc@freeuk.com> wrote:
>
>
>"Bruce Evans" <bde@besplex.bde.org> wrote in message
>news:lkvgvv$19v$1@dont-email.me...
>> In article <JJtcv.95786$Sp6.12404@fx15.am4>, BartC <bc@freeuk.com> wrote:
>>>"Rich" <rich@example.com> wrote in message
>>>news:537246b1$0$59749$c3e8da3$e074e489@news.astraweb.com...
>>>> BartC <bc@freeuk.com> wrote:
>>>
>>>>> Given enough precision, I believe that binary floating point can
>>>>> emulate what can be done in decimal arithmetic.
>>>>
>>>> Actually, no.
>>
>> Actually, yes.
>>
>>>> 0.1 in decimal is an infinite repeating sequence in binary floating
>>>> point.  So it is impossible to have enough precision to exactly
>>>> represent 0.1 (decimal) in binary floating point.
>>
>> 10.0 (0.1 dollars in cents) is equally exact all reasonable
>> implementations
>> of floating point.
>
>So what? You don't use the primitive '=' (or '==' in C) between floating
>point values when you know the results are not absolutely precise.

I do if I know the result is exact and the arithmetic is good enough to
do the right thing.  Comparing for exactly 0.0 is common.  In fact, you
did it in myfloat_round_dec().  This depends on the arithmetic being
good enough to do the right thing with 0.0.

>>>The following is an example of how it can be done (see main()). Actually,
>>>you can replace the definition of 'myfloat' with just 'float' instead of
>>>'double', and it will still work (but up to a smaller magnitude). This
>>>code
>>>emulates some decimal arithmetic with two decimal places. (Note that the
>>>normal 0.1+0.2==0.3 can give True with some compilers.)
>>
>> Giving True is probably a compiler bug.
>
>Not really. Probably just a coincidence. For example, if any constant 0.1 to
>0.9 in the code is constructed as a multiple of 0.1, then it could well be
>the case that 0.1+0.2 has exactly the same representation as 0.3.

The coincidence is not very likely to occur.  I think it is 1 in 2 for
random decimal fractions if double expressions are evaluated in double
precision, and 1 in 4096 for random decimal fractions if double expressions
are evaluated in 11 bits of extra precision.  In fact, it doesn't occur
for 0.1 + 0.2 in IEEE754 double arithmetic:

#include <fenv.h>

volatile double x = 0.1;
volatile double y = 0.2;
volatile double z = 0.3;
volatile double r;

int
main(void)
{
	fesetround(FE_TONEAREST);
	r = x + y;
	printf("RN: 0.1 + 0.2 = %.13a, 0.3 = %.13a\n", r, z);
	fesetround(FE_UPWARD);
	r = x + y;
	printf("RP: 0.1 + 0.2 = %.13a, 0.3 = %.13a\n", r, z);
	fesetround(FE_DOWNWARD);
	r = x + y;
	printf("RM: 0.1 + 0.2 = %.13a, 0.3 = %.13a\n", r, z);
	fesetround(FE_TOWARDZERO);
	r = x + y;
	printf("RZ: 0.1 + 0.2 = %.13a, 0.3 = %.13a\n", r, z);
	return (0);
}

RN: 0.1 + 0.2 = 0x1.3333333333334p-2, 0.3 = 0x1.3333333333333p-2
RP: 0.1 + 0.2 = 0x1.3333333333334p-2, 0.3 = 0x1.3333333333333p-2
RM: 0.1 + 0.2 = 0x1.3333333333333p-2, 0.3 = 0x1.3333333333333p-2
RZ: 0.1 + 0.2 = 0x1.3333333333333p-2, 0.3 = 0x1.3333333333333p-2

The default rounding mode in C99 with IEEE754 extensions is to nearest.
I initialized to this, and also used volatile variables, to be sure.

But the rounding happens to go the other way in 2 out of 4 rounding modes.

Next there is the question of whether the constant expression
(0.1 + 0.2 == 0.3) written by a naive programmer, or one selling decimal
arithmetic, or one lecturing on floating point arithmetic, has the same
value as the expressions with volatile variables that I wrote (I didn't
actually right the corresponding expression.  It would be (r = z)).  In
general, it doesn't.  However, if double expressions are evaluated in
double precision, and the C is C99 with IEEE754 extensions, and there
are no compiler bugs, then the result is the same as for the runtime
calculation with rounding mode FE_TONEAREST, since that mode is specified
to apply at translation time.  So it would be a compiler bug for
(0.1 + 0.2 == 0.3) to be true then.  Howver, in C99 without IEEE754,
the rounding mode for translation time is implementation-defined, so
(0.1 + 0.2 == 0.3) could be either true or false.

>>>/* Floating emulation of fixed point decimal arithmetic */
>>
>> Too complicated.  Just do everything in whole cents (or whatever the
>> minimal unit in the currency is).
>
>The emulation could have used whole units, sure, with input or output scaled
>by the right number of decimal places. But it was tidier in this simple demo
>to have the stored floating point value be the same as the emulated decimal 
>without scaling. Also you can use floating point constants within the code 
>as 0.1 without a scale function needed.

I don't see how to control the error easily then.  Rounding to a multiple
of 0.01 after every operation would be painful, and rounding to the
multiple at critical points would risk missing some critical points.

>>  In C, require C99 or later, with
>> IEEE754 support (otherwise you cannot even depend on 1.0 + 1.0 being
>> 2.0).
>
>That sounds like a bad implementation, but shouldn't effect the results of
>programs like this.

You can't even write programs like this if the implementation is
sufficiently bad.

>>>myfloat round_dec(myfloat x){
>>>if (x>0.0)
>>> return floor((x+rounding)*factor)/factor;
>>>else
>>> if (x>-rounding) return 0.0;
>>> return ceil((x-rounding)*factor)/factor;
>>>}
>>
>> This only works when the error in the FP calculations is slightly less
>> than 0.5 cents.
>
>That's the trick. If you are emulating some hardware or software that always
>has intermediate results with two decimal places (as in my example), then
>you do the same. For simple addition or subtraction, you might have to do
>large numbers of successive operations, each without rounding, before the
>cumulative error approaches 0.005.
>
>With intermediate rounding at each step, the error will rarely be that
>large.

Yes, it only works when the error is controlled.

The above illustrates how to implement the function, but I would avoid
using floor() and ceil().  round(x*100)/100 does the same thing a little
more accurately.  Production quality code also has to worry about x*100
overflowing when calculating the world's GDP in cents.

>> Interest rate calculations are one source of half-way cases.  The
>> interest rate is presumably specified as a decimal fraction, say
>> 5%/year.  Suppose the principal is $1.10.  The interest in infinite
>> precision is 5.5 cents.  This must be rounded according to
>> financial rules.  I don't know what they are, but presumably they
>> favour bankers, so it is down to 5 cents.  Suppose you calculated
>> the principal dollars an have a tiny error, so it is $1.10000...1.
>> Then the interest calculation gives 5.50000...5 cents.  This is
>> above the half-way point, so hopefully it is not rounded in the
>> banker's favour and is rounded up to 6 cents.  However, that would
>> be illegal since the error is in the calculation.
>
>Floating point gives you the opportunity to round to either 5.0 or 6.0
>cents, it doesn't really matter, and remember the approximate 0.5 cents and
>add it to next year's interest. Nobody loses out in the long run. Decimal
>fixed point can end up discarding some fractions of a cent completely.
>Unless you use decimal floating point, then you have the same rounding
>problems.

I think the accounting rules don't allow it to be off by even 1 cent per
$billion, although this case doesn't matter.  The exact rules matter when
it is 1 billion transactions of 5.5 cents each, with a rounding error of
0.5 cents in someone's favour on each one.  The 0.5 part must be calculated
exactly on each one.  This is easy using decimal FP.  With binary FP, I
don't see anything better than scaling to give basically fixed point
decimal with the scale not really fixed but depending on the transaction
type.

>But to solve the above problem, requires a bit more thought. For example, if
>interest rates are quoted to a resolution of 0.1% (ie. 0.001), and principal
>amounts to $0.01, then any such calculation can only give results to 5
>decimal places (but I'm not a mathematician so cannot say for certain).
>
>In other words, any result must be to the nearest $0.00001. Provided there 
>is enough floating point precision in reserve, it can make use of this fact 
>to apply the right kind of rounding.

Yes, it is a bit tricky to scale to get enough acurracy.  It is easiest
to scale to get perfect accuracy.  I calculated that 64 bits is just
enough for world GDP calculations with weird interest rates.  You could
also use integer 64-bit arithmetic but then rescaling is harder.  You
could use mostly integer arithmetic with FP to do the rescaling...

>In your example, in one instance I got a result of $0.05500000000000000700,
>rounded to 0.05500 (to 1/1000 of a cent). Now I can apply a rounding
>function more confidently: any value less than 0.054995 can be rounded down
>to 0.05000, and above 0.055005 to 0.06000, assuming rounding rules are to
>the nearest cent. But I now have a window within which the interest rate 
>calculation is expected to be, and that gives me leaway for any FP 
>inaccuracies.

I mentioned using (C99 IEEE754) fenv stuff to check for accuracy.  It is
only easy to use to check for perfect accuracy.  Change the scale as
necessary so that everything is exact (an integer in some power-of-10
units).  Then check for FE_INEXACT exceptions to find all the cases
that you missed.

Bruce

Back to comp.misc | Previous | Next — Previous in thread | Next in thread | Find similar | Unroll thread


Thread

BASIC is 50 Gordon Henderson <gordon+usenet@drogon.net> - 2014-04-30 09:34 +0000
  Re: BASIC is 50 Ant <ant@zimage.comANT> - 2014-04-30 03:42 -0700
    Re: BASIC is 50 Bob Eager <news0002@eager.cx> - 2014-04-30 10:46 +0000
      Re: BASIC is 50 Gordon Henderson <gordon+usenet@drogon.net> - 2014-04-30 12:03 +0000
  Re: BASIC is 50 Rich <rich@example.com> - 2014-04-30 11:12 +0000
    Re: BASIC is 50 scott@alfter.diespammersdie.us (Scott Alfter) - 2014-04-30 15:56 +0000
      Re: BASIC is 50 Rich <rich@example.com> - 2014-04-30 20:41 +0000
        Re: BASIC is 50 Will Honea <whonea@whonea.net> - 2014-04-30 18:38 -0600
          Re: BASIC is 50 Rich <rich@example.com> - 2014-05-01 02:38 +0000
            Re: BASIC is 50 Will Honea <whonea@whonea.net> - 2014-04-30 22:50 -0600
      Re: BASIC is 50 Rich <rich@example.com> - 2014-04-30 20:48 +0000
        Re: BASIC is 50 Richard Kettlewell <rjk@greenend.org.uk> - 2014-05-02 09:27 +0100
          Re: BASIC is 50 Tim Streater <timstreater@greenbee.net> - 2014-05-02 09:50 +0100
        Re: BASIC is 50 "BartC" <bc@freeuk.com> - 2014-05-07 10:48 +0100
          Re: BASIC is 50 Tim Streater <timstreater@greenbee.net> - 2014-05-07 10:57 +0100
          Re: BASIC is 50 Huge <Huge@nowhere.much.invalid> - 2014-05-07 11:20 +0000
            Re: BASIC is 50 "BartC" <bc@freeuk.com> - 2014-05-07 13:14 +0100
              Re: BASIC is 50 Marko Rauhamaa <marko@pacujo.net> - 2014-05-07 16:05 +0300
                Re: BASIC is 50 Paul Sture <nospam@sture.ch> - 2014-05-08 18:06 +0200
                Re: BASIC is 50 Marko Rauhamaa <marko@pacujo.net> - 2014-05-08 20:17 +0300
              Re: BASIC is 50 "James Harris" <james.harris.1@gmail.com> - 2014-05-07 16:08 +0100
                Re: BASIC is 50 "BartC" <bc@freeuk.com> - 2014-05-07 18:38 +0100
                Re: BASIC is 50 "James Harris" <james.harris.1@gmail.com> - 2014-05-07 19:09 +0100
                Re: BASIC is 50 Huge <Huge@nowhere.much.invalid> - 2014-05-07 19:08 +0000
              Re: BASIC is 50 greymausg@mail.com - 2014-05-08 10:15 +0000
                Re: BASIC is 50 "BartC" <bc@freeuk.com> - 2014-05-08 11:38 +0100
              Re: BASIC is 50 Paul Sture <nospam@sture.ch> - 2014-05-08 17:55 +0200
          Re: BASIC is 50 Paul Sture <nospam@sture.ch> - 2014-05-08 17:44 +0200
          Re: BASIC is 50 Rich <rich@example.com> - 2014-05-12 12:39 +0000
            Re: BASIC is 50 Marko Rauhamaa <marko@pacujo.net> - 2014-05-12 16:06 +0300
            Re: BASIC is 50 Ben Bacarisse <ben.usenet@bsb.me.uk> - 2014-05-12 22:48 +0100
              Re: BASIC is 50 Rich <rich@example.com> - 2014-05-13 12:03 +0000
                Re: BASIC is 50 "BartC" <bc@freeuk.com> - 2014-05-13 13:23 +0100
                Re: BASIC is 50 Rich <rich@example.com> - 2014-05-13 16:22 +0000
                Re: BASIC is 50 "BartC" <bc@freeuk.com> - 2014-05-13 19:42 +0100
                Re: BASIC is 50 "BartC" <bc@freeuk.com> - 2014-05-13 19:49 +0100
                Re: BASIC is 50 Rich <rich@example.com> - 2014-05-14 01:09 +0000
                Re: BASIC is 50 "BartC" <bc@freeuk.com> - 2014-05-14 09:51 +0100
                Re: BASIC is 50 bde@besplex.bde.org (Bruce Evans) - 2014-05-14 10:36 +0000
                Re: BASIC is 50 "BartC" <bc@freeuk.com> - 2014-05-14 12:58 +0100
                Re: BASIC is 50 bde@besplex.bde.org (Bruce Evans) - 2014-05-14 16:13 +0000
                Re: BASIC is 50 "BartC" <bc@freeuk.com> - 2014-05-14 19:24 +0100
                Re: BASIC is 50 Ben Bacarisse <ben.usenet@bsb.me.uk> - 2014-05-13 15:50 +0100
    Re: BASIC is 50 Johny B Good <johnny-b-good@invalid.ntlworld.com> - 2014-05-01 02:50 +0100
    Re: BASIC is 50 David Hume <David.Hume@example.com> - 2014-05-01 09:06 +0100
      Re: BASIC is 50 Gordon Henderson <gordon+usenet@drogon.net> - 2014-05-01 09:56 +0000
      Re: BASIC is 50 Rich <rich@example.com> - 2014-05-01 10:25 +0000
        Re: BASIC is 50 Marko Rauhamaa <marko@pacujo.net> - 2014-05-01 16:43 +0300
          Re: BASIC is 50 Gordon Henderson <gordon+usenet@drogon.net> - 2014-05-01 18:03 +0000
          Re: BASIC is 50 Bob Eager <news0002@eager.cx> - 2014-05-01 21:35 +0000
        Re: BASIC is 50 Rob <monst.oorzudo@ntlworld.com> - 2014-05-01 22:41 +0100
          Re: BASIC is 50 David Hume <David.Hume@example.com> - 2014-05-02 09:26 +0100
            Re: BASIC is 50 Hils <hils@saynotospam.net> - 2014-05-02 10:46 +0100
            Re: BASIC is 50 Johny B Good <johnny-b-good@invalid.ntlworld.com> - 2014-05-02 23:11 +0100
  Re: BASIC is 50 Anssi Saari <as@sci.fi> - 2014-04-30 17:51 +0300
  Re: BASIC is 50 Peter Percival <peterxpercival@hotmail.com> - 2014-05-14 17:40 +0100

csiph-web