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


Groups > comp.lang.c > #138944 > unrolled thread

A final word on casting.

Started bychanrasjid@gmail.com
First post2018-12-23 03:02 -0800
Last post2018-12-24 22:36 -0800
Articles 20 on this page of 62 — 17 participants

Back to article view | Back to comp.lang.c


Contents

  A final word on casting. chanrasjid@gmail.com - 2018-12-23 03:02 -0800
    Re: A final word on casting. Bart <bc@freeuk.com> - 2018-12-23 12:11 +0000
      Re: A final word on casting. Ben Bacarisse <ben.usenet@bsb.me.uk> - 2018-12-23 13:08 +0000
        Re: A final word on casting. Bart <bc@freeuk.com> - 2018-12-23 14:43 +0000
          Re: A final word on casting. Ben Bacarisse <ben.usenet@bsb.me.uk> - 2018-12-23 14:56 +0000
            Re: A final word on casting. chanrasjid@gmail.com - 2018-12-23 07:08 -0800
              Re: A final word on casting. Bart <bc@freeuk.com> - 2018-12-23 15:55 +0000
                Re: A final word on casting. Bart <bc@freeuk.com> - 2018-12-23 16:07 +0000
                Re: A final word on casting. James Kuyper <jameskuyper@alumni.caltech.edu> - 2018-12-23 08:21 -0800
                Re: A final word on casting. Ben Bacarisse <ben.usenet@bsb.me.uk> - 2018-12-23 17:56 +0000
                  Re: A final word on casting. Bart <bc@freeuk.com> - 2018-12-23 19:23 +0000
                    Re: A final word on casting. Ben Bacarisse <ben.usenet@bsb.me.uk> - 2018-12-23 20:12 +0000
                    Re: A final word on casting. James Kuyper <jameskuyper@alumni.caltech.edu> - 2018-12-23 14:06 -0800
                    Re: A final word on casting. Tim Rentsch <txr@alumni.caltech.edu> - 2018-12-28 09:34 -0800
                      Re: A final word on casting. Bart <bc@freeuk.com> - 2018-12-28 20:29 +0000
                        Re: A final word on casting. Tim Rentsch <txr@alumni.caltech.edu> - 2018-12-28 15:07 -0800
                          Re: A final word on casting. Richard Damon <Richard@Damon-Family.org> - 2018-12-28 20:56 -0500
                            Re: A final word on casting. Tim Rentsch <txr@alumni.caltech.edu> - 2018-12-29 09:22 -0800
                              Re: A final word on casting. Tim Rentsch <txr@alumni.caltech.edu> - 2019-01-01 13:27 -0800
                                Re: A final word on casting. Bart <bc@freeuk.com> - 2019-01-02 01:07 +0000
                                  Re: A final word on casting. Bart <bc@freeuk.com> - 2019-01-03 18:55 +0000
                                    Re: A final word on casting. Richard Damon <Richard@Damon-Family.org> - 2019-01-03 22:27 -0500
                                      Re: A final word on casting. James Kuyper <jameskuyper@alumni.caltech.edu> - 2019-01-04 16:25 -0500
                    Re: A final word on casting. Ike Naar <ike@iceland.freeshell.org> - 2018-12-28 19:10 +0000
                      Re: A final word on casting. James Kuyper <jameskuyper@alumni.caltech.edu> - 2018-12-28 12:33 -0800
                  Re: A final word on casting. "Chris M. Thomasson" <invalid_chris_thomasson@invalid.invalid> - 2018-12-23 14:11 -0800
                  Re: A final word on casting. Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2018-12-24 02:54 -0800
                    Re: A final word on casting. fir <profesor.fir@gmail.com> - 2018-12-24 03:24 -0800
                      Re: A final word on casting. fir <profesor.fir@gmail.com> - 2018-12-24 03:29 -0800
                        Re: A final word on casting. fir <profesor.fir@gmail.com> - 2018-12-24 03:45 -0800
                    Re: A final word on casting. Ben Bacarisse <ben.usenet@bsb.me.uk> - 2018-12-24 13:44 +0000
                    Re: A final word on casting. Richard Damon <Richard@Damon-Family.org> - 2018-12-24 08:51 -0500
                Re: A final word on casting. Richard Damon <Richard@Damon-Family.org> - 2018-12-23 14:53 -0500
                  Re: A final word on casting. Robert Wessel <robertwessel2@yahoo.com> - 2018-12-24 09:39 -0600
                    Re: A final word on casting. Richard Damon <Richard@Damon-Family.org> - 2018-12-24 13:38 -0500
                Re: A final word on casting. Manfred <noname@add.invalid> - 2018-12-23 21:22 +0100
                Re: A final word on casting. Manfred <noname@add.invalid> - 2018-12-23 21:33 +0100
              Re: A final word on casting. James Kuyper <jameskuyper@alumni.caltech.edu> - 2018-12-23 10:56 -0500
                Re: A final word on casting. Sjouke Burry <burrynulnulfour@ppllaanneett.nnll> - 2018-12-23 17:18 +0100
          Re: A final word on casting. Bart <bc@freeuk.com> - 2018-12-23 20:03 +0000
      Re: A final word on casting. "Rick C. Hodgin" <rick.c.hodgin@gmail.com> - 2018-12-28 14:47 -0500
    Re: A final word on casting. Ben Bacarisse <ben.usenet@bsb.me.uk> - 2018-12-23 12:24 +0000
    Re: A final word on casting. Richard Damon <Richard@Damon-Family.org> - 2018-12-23 07:28 -0500
      Re: A final word on casting. Tim Rentsch <txr@alumni.caltech.edu> - 2018-12-28 07:50 -0800
    Re: A final word on casting. fir <profesor.fir@gmail.com> - 2018-12-23 04:35 -0800
      Re: A final word on casting. fir <profesor.fir@gmail.com> - 2018-12-23 04:42 -0800
        Re: A final word on casting. fir <profesor.fir@gmail.com> - 2018-12-23 04:52 -0800
    Re: A final word on casting. James Kuyper <jameskuyper@alumni.caltech.edu> - 2018-12-23 10:28 -0500
    Re: A final word on casting. Barry Schwarz <schwarzb@dqel.com> - 2018-12-23 11:39 -0800
      Re: A final word on casting. Ike Naar <ike@iceland.freeshell.org> - 2018-12-24 07:56 +0000
    Re: A final word on casting. chanrasjid@gmail.com - 2018-12-24 14:03 -0800
      Re: A final word on casting. Barry Schwarz <schwarzb@dqel.com> - 2018-12-24 14:39 -0800
        Re: A final word on casting. chanrasjid@gmail.com - 2018-12-24 14:50 -0800
          Re: A final word on casting. Keith Thompson <kst-u@mib.org> - 2018-12-24 15:34 -0800
        Re: A final word on casting. Keith Thompson <kst-u@mib.org> - 2018-12-24 15:23 -0800
          Re: A final word on casting. chanrasjid@gmail.com - 2018-12-24 20:34 -0800
      Re: A final word on casting. Keith Thompson <kst-u@mib.org> - 2018-12-24 14:52 -0800
        Re: A final word on casting. chanrasjid@gmail.com - 2018-12-24 20:25 -0800
      Re: A final word on casting. James Kuyper <jameskuyper@alumni.caltech.edu> - 2018-12-24 17:52 -0500
      Re: A final word on casting. luser droog <luser.droog@gmail.com> - 2018-12-24 21:27 -0800
        Re: A final word on casting. luser droog <luser.droog@gmail.com> - 2018-12-24 21:39 -0800
        Re: A final word on casting. chanrasjid@gmail.com - 2018-12-24 22:36 -0800

Page 1 of 4  [1] 2 3 4  Next page →


#138944 — A final word on casting.

Fromchanrasjid@gmail.com
Date2018-12-23 03:02 -0800
SubjectA final word on casting.
Message-ID<af569436-b2e1-492b-8379-9f927013c590@googlegroups.com>
I have been playing with C for about 30 years, but I could never get to really understand casting. I made various weak attempts and gave up; I knew I don't need to be professional in C.

I take my layman approach to casting: it is to represent the value of a certain type in a different data type. 

Some things I tested work and I don't care about its correctness. I always get it correct: (int) 1.23 always give 1. But if we reason things out, if casting tries to represent a float as any integer type, it is not possible. So C may here just return the integral part; but it seems this is not "official".

Casting a signed int to a long unsigned integer is meaningless if the original integer is negative; I think the experts know what result would be returned as per some C standards; but then what is the purpose when it is meaningless.

The only use I think is safe is casting compatible types to another with the larger storage, as int to _int64, or float to double. Casting double to float is OK as we expect a loss of precision.

[toc] | [next] | [standalone]


#138945

FromBart <bc@freeuk.com>
Date2018-12-23 12:11 +0000
Message-ID<1YKTD.30400$CD3.15082@fx05.am4>
In reply to#138944
On 23/12/2018 11:02, chanrasjid@gmail.com wrote:
> I have been playing with C for about 30 years, but I could never get to really understand casting. I made various weak attempts and gave up; I knew I don't need to be professional in C.
> 
> I take my layman approach to casting: it is to represent the value of a certain type in a different data type.
> 
> Some things I tested work and I don't care about its correctness. I always get it correct: (int) 1.23 always give 1.

Try:

printf("%d\n", (int)1.9999999999999999999999999999999);

I seem to get the answer 2.

> Casting a signed int to a long unsigned integer is meaningless if the original integer is negative; I think the experts know what result would be returned as per some C standards; but then what is the purpose when it is meaningless.

It seems to be the same as (unsigned long long)(signed long long int)(a) 
when a is signed int. So, sign-extend a to the new width, then interpret 
the bits as unsigned.

> The only use I think is safe is casting compatible types to another with the larger storage, as int to _int64, or float to double. Casting double to float is OK as we expect a loss of precision.

Double has a wider magnitude range than float, so it can fail that way 
(the float value ends up as infinity when the magnitude is too big).

-- 
bart

[toc] | [prev] | [next] | [standalone]


#138954

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2018-12-23 13:08 +0000
Message-ID<87k1k0ie66.fsf@bsb.me.uk>
In reply to#138945
Bart <bc@freeuk.com> writes:

> On 23/12/2018 11:02, chanrasjid@gmail.com wrote:
>> I have been playing with C for about 30 years, but I could never get to really understand casting. I made various weak attempts and gave up; I knew I don't need to be professional in C.
>>
>> I take my layman approach to casting: it is to represent the value of a certain type in a different data type.
>>
>> Some things I tested work and I don't care about its correctness. I always get it correct: (int) 1.23 always give 1.
>
> Try:
>
> printf("%d\n", (int)1.9999999999999999999999999999999);
>
> I seem to get the answer 2.

Are you really puzzled by that?

A C compiler has a little leeway in what value to give to that decimal
constant has, but most implementations will pick the closest value of
type double, which is 2.

> It seems to be the same as (unsigned long long)(signed long long
> int)(a) when a is signed int. So, sign-extend a to the new width, then
> interpret the bits as unsigned.

The C standard gives the rule, and it is not based on sign-extending and
interpreting bits.  But for all the machines you care about, the result
will be as you say.

for what it's worth, the rule is:

  "... the value is converted by repeatedly adding or subtracting one
  more than the maximum value that can be represented in the new type
  until the value is in the range of the new type."

i.e. it's x mod 2**n where n is the number of value bits in the unsigned
type.

>> The only use I think is safe is casting compatible types to another
>> with the larger storage, as int to _int64, or float to
>> double. Casting double to float is OK as we expect a loss of
>> precision.
>
> Double has a wider magnitude range than float, so it can fail that way
> (the float value ends up as infinity when the magnitude is too big).

double usually has a wider range that float but it's no guaranteed.  And
the floating point type does not necessarily have an infinity.  But on
all the machines you care about, what you say is probably true.

-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#138961

FromBart <bc@freeuk.com>
Date2018-12-23 14:43 +0000
Message-ID<NaNTD.109597$krL1.91@fx15.am4>
In reply to#138954
On 23/12/2018 13:08, Ben Bacarisse wrote:
> Bart <bc@freeuk.com> writes:

>> Try:
>>
>> printf("%d\n", (int)1.9999999999999999999999999999999);
>>
>> I seem to get the answer 2.
> 
> Are you really puzzled by that?

Actually, yes. It means that (int)1.something will usually be 1 but 
sometimes 2.

(I tried my own language to see if it does something different, and that 
was worse as it seems to round to nearest int value [for x64 target 
using 'cvtsd2si'], so this effect happens when you try the equivalent of 
(int)1.4999999999999999999999.

However, with my big float library, the equivalent of 
(int)1.99999999999... will always give 1 no matter how many 9s there are.)

> double usually has a wider range that float but it's no guaranteed.  And
> the floating point type does not necessarily have an infinity.  But on
> all the machines you care about, what you say is probably true.

The OP's comment about there being loss of precision from double to 
float suggested they were talking about when float was a narrower type.

Usually that means the exponent is also narrower. (Not always; I've used 
a software float32 type - the top half of a float64 - where the exponent 
range is the same.)

[toc] | [prev] | [next] | [standalone]


#138963

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2018-12-23 14:56 +0000
Message-ID<875zvki950.fsf@bsb.me.uk>
In reply to#138961
Bart <bc@freeuk.com> writes:

> On 23/12/2018 13:08, Ben Bacarisse wrote:
>> Bart <bc@freeuk.com> writes:
>
>>> Try:
>>>
>>> printf("%d\n", (int)1.9999999999999999999999999999999);
>>>
>>> I seem to get the answer 2.
>>
>> Are you really puzzled by that?
>
> Actually, yes. It means that (int)1.something will usually be 1 but
> sometimes 2.

At least you won't be puzzled any more.

-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#138965

Fromchanrasjid@gmail.com
Date2018-12-23 07:08 -0800
Message-ID<fabc96a8-762e-430e-a567-8658f60fe0d5@googlegroups.com>
In reply to#138963
On Sunday, December 23, 2018 at 10:57:14 PM UTC+8, Ben Bacarisse wrote:
> Bart <bc@freeuk.com> writes:
> 
> > On 23/12/2018 13:08, Ben Bacarisse wrote:
> >> Bart <bc@freeuk.com> writes:
> >
> >>> Try:
> >>>
> >>> printf("%d\n", (int)1.9999999999999999999999999999999);
> >>>
> >>> I seem to get the answer 2.
> >>
> >> Are you really puzzled by that?
> >
> > Actually, yes. It means that (int)1.something will usually be 1 but
> > sometimes 2.
> 
> At least you won't be puzzled any more.
> 
> -- 
> Ben.

I think it is reasonable. C gives the closest double to represent 1.999... and it is 2.0; So (int) x gives the integral part is "almost" official. 

[toc] | [prev] | [next] | [standalone]


#138967

FromBart <bc@freeuk.com>
Date2018-12-23 15:55 +0000
Message-ID<QdOTD.34006$Ck1.30728@fx18.am4>
In reply to#138965
On 23/12/2018 15:08, chanrasjid@gmail.com wrote:
> On Sunday, December 23, 2018 at 10:57:14 PM UTC+8, Ben Bacarisse wrote:
>> Bart <bc@freeuk.com> writes:
>>
>>> On 23/12/2018 13:08, Ben Bacarisse wrote:
>>>> Bart <bc@freeuk.com> writes:
>>>
>>>>> Try:
>>>>>
>>>>> printf("%d\n", (int)1.9999999999999999999999999999999);
>>>>>
>>>>> I seem to get the answer 2.
>>>>
>>>> Are you really puzzled by that?
>>>
>>> Actually, yes. It means that (int)1.something will usually be 1 but
>>> sometimes 2.
>>
>> At least you won't be puzzled any more.

Don't be so sure.

> I think it is reasonable. C gives the closest double to represent 1.999... and it is 2.0; So (int) x gives the integral part is "almost" official.

How does "1.999999999999..." in source code end up as /exactly/ 2.0?

Try this program, which starts from 1.0 then successively ends 0.9, 
0.09, 0.009 etc to get progressively longer versions of 1.999...

It gets closer and closer to 2.0, but is never exactly 2.0:

   #include <stdio.h>

   int main (void) {
     double a,b;

     a=1.0;
     b=0.9;
     for (int i=0; i<30; ++i) {
         a+=b;
         printf("a: %.24f, 2.0-a: %.10e, a%s2.0, (int)a: %d, b: %e\n",
              a, 2.0-a, (a==2.0?"==":"!="), (int)a, b);
         b/=10.0;
     }
   }

So it appears to be either rounding deliberately applied by a C 
compiler, or a side-effect of how the calculation is actually done.

-- 
bart

[toc] | [prev] | [next] | [standalone]


#138969

FromBart <bc@freeuk.com>
Date2018-12-23 16:07 +0000
Message-ID<mpOTD.34634$8Q1.20400@fx14.am4>
In reply to#138967
On 23/12/2018 15:55, Bart wrote:

> Try this program, which starts from 1.0 then successively ends 0.9, 
> 0.09, 0.009 etc to get progressively longer versions of 1.999...
> 
> It gets closer and closer to 2.0, but is never exactly 2.0:
> 
>    #include <stdio.h>
> 
>    int main (void) {
>      double a,b;
> 
>      a=1.0;
>      b=0.9;
>      for (int i=0; i<30; ++i) {
>          a+=b;
>          printf("a: %.24f, 2.0-a: %.10e, a%s2.0, (int)a: %d, b: %e\n",

The format for b should really show at least 15 digits, so use at least 
%.15e. On my machine, the actual b value starts off as just over 0.9, 
but ends marginally below 0.000...009.

With such values, it's not surprising that the accumulated value can't 
be pushed past 2.0.

[toc] | [prev] | [next] | [standalone]


#138972

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2018-12-23 08:21 -0800
Message-ID<3191a370-3e4f-4216-a011-1d5b5d72c05f@googlegroups.com>
In reply to#138967
On Sunday, December 23, 2018 at 10:55:36 AM UTC-5, Bart wrote:
> On 23/12/2018 15:08, chanrasjid@gmail.com wrote:
> > On Sunday, December 23, 2018 at 10:57:14 PM UTC+8, Ben Bacarisse wrote:
> >> Bart <bc@freeuk.com> writes:
> >>
> >>> On 23/12/2018 13:08, Ben Bacarisse wrote:
> >>>> Bart <bc@freeuk.com> writes:
> >>>
> >>>>> Try:
> >>>>>
> >>>>> printf("%d\n", (int)1.9999999999999999999999999999999);
> >>>>>
> >>>>> I seem to get the answer 2.
> >>>>
> >>>> Are you really puzzled by that?
> >>>
> >>> Actually, yes. It means that (int)1.something will usually be 1 but
> >>> sometimes 2.
> >>
> >> At least you won't be puzzled any more.
> 
> Don't be so sure.
> 
> > I think it is reasonable. C gives the closest double to represent 1.999... and it is 2.0; So (int) x gives the integral part is "almost" official.
> 
> How does "1.999999999999..." in source code end up as /exactly/ 2.0?
You originally gave 1.9999999999999999999999999999999.
1.999999999999 wouldn't produce that result on most systems, but
1.9999999999999999 would. For example, on my system,
nextafter(2.0,0.0) is 1.99999999999999977796. 1.9999999999999999
is closer to 2.0 than it is to 1.99999999999999977796, so 2.0 is
the value represented by that constant. Would you prefer
1.9999999999999999 to have a value of 1.99999999999999977796?
Which is more important: that the value be less than 2.0, or that
it represent the provided digits as closely as possible? 2.0 and 1.99999999999999977796 are the two best options - representing it
more accurately than either of those two values isn't an
available option.

[toc] | [prev] | [next] | [standalone]


#138978

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2018-12-23 17:56 +0000
Message-ID<87pntsgm97.fsf@bsb.me.uk>
In reply to#138967
Bart <bc@freeuk.com> writes:

> On 23/12/2018 15:08, chanrasjid@gmail.com wrote:
>> On Sunday, December 23, 2018 at 10:57:14 PM UTC+8, Ben Bacarisse wrote:
>>> Bart <bc@freeuk.com> writes:
>>>
>>>> On 23/12/2018 13:08, Ben Bacarisse wrote:
>>>>> Bart <bc@freeuk.com> writes:
>>>>
>>>>>> Try:
>>>>>>
>>>>>> printf("%d\n", (int)1.9999999999999999999999999999999);
>>>>>>
>>>>>> I seem to get the answer 2.
>>>>>
>>>>> Are you really puzzled by that?
>>>>
>>>> Actually, yes. It means that (int)1.something will usually be 1 but
>>>> sometimes 2.
>>>
>>> At least you won't be puzzled any more.
>
> Don't be so sure.
>
>> I think it is reasonable. C gives the closest double to represent 1.999... and it is 2.0; So (int) x gives the integral part is "almost" official.
>
> How does "1.999999999999..." in source code end up as /exactly/ 2.0?

Because the compiler is giving you the closest double to the number you
wrote.

That constant /could/ give a value less than two because C only requires
the result to be either the closest double, or one of the two double
values adjacent to the closest.  I.e. C permits a little wiggle room for
implementations (but the implementation must document what happens).

> Try this program, which starts from 1.0 then successively ends 0.9,
> 0.09, 0.009 etc to get progressively longer versions of 1.999...
>
> It gets closer and closer to 2.0, but is never exactly 2.0:

If you are going to do numerical work, you would need to understand
what's happening but it's much simpler, for me, to leave you puzzled.  I
am not at all sure you really want to understand.  If I'm wrong, and
this is a part of C you'd like to learn, I will gladly explain.

-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#138982

FromBart <bc@freeuk.com>
Date2018-12-23 19:23 +0000
Message-ID<4hRTD.59185$M61.9999@fx19.am4>
In reply to#138978
On 23/12/2018 17:56, Ben Bacarisse wrote:
> Bart <bc@freeuk.com> writes:

>> How does "1.999999999999..." in source code end up as /exactly/ 2.0?
> 
> Because the compiler is giving you the closest double to the number you
> wrote.

You say that so glibly that anyone reading it would just it take at face 
value.

What it doesn't explain is how a compiler, presented with a complex set 
of lossy calculations to arrive at that result, and armed only with the 
same 64-bit set of binary floating point operations, actually achieves it.

My own C compiler gets this answer, (exactly 2.0 rather than the next 
value below which is, I believe, 
1.9999999999999997779553950749686919152736663818359375), but that is 
largely by chance. Generally, floating point constants might differ from 
mature compilers like gcc by one least significant bit.

> If you are going to do numerical work, you would need to understand
> what's happening but it's much simpler, for me, to leave you puzzled.

I know what's happening here. What I don't know is the approach needed 
to turn a string of decimal digits into the closest possible ieee 64-bit 
float value.

> am not at all sure you really want to understand.  If I'm wrong, and
> this is a part of C you'd like to learn, I will gladly explain.

Whatever the explanation, it still doesn't prevent a program like this:

     printf("%d\n",(int)1.999999999);
     printf("%d\n",(int)1.9999999999);
     printf("%d\n",(int)1.99999999999);
     printf("%d\n",(int)1.999999999999);
     printf("%d\n",(int)1.9999999999999);
     printf("%d\n",(int)1.99999999999999);
     printf("%d\n",(int)1.999999999999999);
     printf("%d\n",(int)1.9999999999999999);
     printf("%d\n",(int)1.99999999999999999);
     printf("%d\n",(int)1.999999999999999999);
     printf("%d\n",(int)1.9999999999999999999);

from producing this output:

     1
     1
     1
     1
     1
     1
     1
     2
     2
     2
     2

or from similar results when you apply floor() to each constant.

[toc] | [prev] | [next] | [standalone]


#138991

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2018-12-23 20:12 +0000
Message-ID<87k1k0gfz7.fsf@bsb.me.uk>
In reply to#138982
Bart <bc@freeuk.com> writes:

> On 23/12/2018 17:56, Ben Bacarisse wrote:
>> Bart <bc@freeuk.com> writes:
>
>>> How does "1.999999999999..." in source code end up as /exactly/ 2.0?
>>
>> Because the compiler is giving you the closest double to the number you
>> wrote.
>
> You say that so glibly that anyone reading it would just it take at
> face value.

Why shouldn't they?  That's what's happening.  But I see that your "how"
was literal -- I took it more like "how come...".

> What it doesn't explain is how a compiler, presented with a complex
> set of lossy calculations to arrive at that result, and armed only
> with the same 64-bit set of binary floating point operations, actually
> achieves it.

You'd have to check the source code for each compiler, but it's likely
that gcc uses strtod on the numeric pre-processing token text.

> My own C compiler gets this answer, (exactly 2.0 rather than the next
> value below which is, I believe,
> 1.9999999999999997779553950749686919152736663818359375), but that is
> largely by chance. Generally, floating point constants might differ
> from mature compilers like gcc by one least significant bit.

I don't like that way of describing what happens because it's very
unlikely that chance is involved.  What you mean is you didn't
deliberately design for this result.

>> If you are going to do numerical work, you would need to understand
>> what's happening but it's much simpler, for me, to leave you puzzled.
>
> I know what's happening here. What I don't know is the approach needed
> to turn a string of decimal digits into the closest possible ieee
> 64-bit float value.

Ah, I see.  I am not enough of an expert to say much about it, but the
leeway given in the standard suggests that it might be hard to get right
on some hardware.

Numerical analysis was once my thing, but that was 40 years ago, in
FORTRAN and on IBM 360 hardware.  Those machines had base-16 floating
point formats that had all sorts of odd behaviours.

>> am not at all sure you really want to understand.  If I'm wrong, and
>> this is a part of C you'd like to learn, I will gladly explain.
>
> Whatever the explanation, it still doesn't prevent a program like this:
>
>     printf("%d\n",(int)1.999999999);
>     printf("%d\n",(int)1.9999999999);
>     printf("%d\n",(int)1.99999999999);
>     printf("%d\n",(int)1.999999999999);
>     printf("%d\n",(int)1.9999999999999);
>     printf("%d\n",(int)1.99999999999999);
>     printf("%d\n",(int)1.999999999999999);
>     printf("%d\n",(int)1.9999999999999999);
>     printf("%d\n",(int)1.99999999999999999);
>     printf("%d\n",(int)1.999999999999999999);
>     printf("%d\n",(int)1.9999999999999999999);
>
> from producing this output:
>
>     1
>     1
>     1
>     1
>     1
>     1
>     1
>     2
>     2
>     2
>     2

I don't see why you worry about this.  Is it not what you want?

-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#139001

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2018-12-23 14:06 -0800
Message-ID<2c7d9440-a265-4119-9ec1-6dbba6ab3ee3@googlegroups.com>
In reply to#138982
On Sunday, December 23, 2018 at 2:23:52 PM UTC-5, Bart wrote:
> On 23/12/2018 17:56, Ben Bacarisse wrote:
> > Bart <bc@freeuk.com> writes:
> 
> >> How does "1.999999999999..." in source code end up as /exactly/ 2.0?
> > 
> > Because the compiler is giving you the closest double to the number you
> > wrote.
> 
> You say that so glibly that anyone reading it would just it take at face 
> value.

That "glib" assertion is based upon two facts. The first fact is that
having the value of the floating point constant be exactly 2.0 is
permitted; it's also permitted to be the next representable value after
2.0. If it has either of those values, the conversion to int would
produce precisely the result you observed. The second fact is that the
only other permitted possibility, the next representable value less than
2.0, would NOT produce the result you observed. It's therefore
guaranteed that the behavior you observed is the result of the constant
having a value >=2.0, and it's a reasonable guess (though not a
certainty) that's it's exactly 2.0.

> What it doesn't explain is how a compiler, presented with a complex set 
> of lossy calculations to arrive at that result, and armed only with the 
> same 64-bit set of binary floating point operations, actually achieves it.

The standard allows for the representable value closest to the exact
value, or the two bracketing representable values. This tells you two
things that the committee believes. Firstly, they believe that it is
feasible to require all implementations to interpret floating point
constants with that much precision. Secondly, that they believe that
imposing a stricter requirement (such as requiring that it always be the
closest representable value) might be an unacceptable burden on at least
some implementations.

The committee is staffed both by representatives of most of the major
compiler vendors, and by expert users of a wide variety of C compilers.
If they had been wrong on either of those points, in the 18 years since
C99 was approved I assume that someone would have been complained about
that fact. Therefore, I recommend carefully considering the possibility
that they are right on both points.

If you don't know how to achieve that much precision, I'd recommend
reading up on the relevant literature. I'm not sure of the algorithm
used to achieve that precision, but I doubt that it's proprietary.

> My own C compiler gets this answer, (exactly 2.0 rather than the next 
> value below which is, I believe, 
> 1.9999999999999997779553950749686919152736663818359375), but that is 
> largely by chance. Generally, floating point constants might differ from 
> mature compilers like gcc by one least significant bit.

Which, if you think about it, might give you a clue as to why the
standard allows any one of three different consecutive representable
values, rather than mandating one particular one of those three values.

> > If you are going to do numerical work, you would need to understand
> > what's happening but it's much simpler, for me, to leave you puzzled.
> 
> I know what's happening here. What I don't know is the approach needed 
> to turn a string of decimal digits into the closest possible ieee 64-bit 
> float value.

The standard only mandates such ideal conversions for hexadecimal
floating point constants, and even then, only if FLT_RADIX is a power of
2. I hope you can figure out why that might be the case. I'll give you
a hint: in that case, you can determine four bits at a time.

> > am not at all sure you really want to understand.  If I'm wrong, and
> > this is a part of C you'd like to learn, I will gladly explain.
> 
> Whatever the explanation, it still doesn't prevent a program like this:
> 
>      printf("%d\n",(int)1.999999999);
>      printf("%d\n",(int)1.9999999999);
>      printf("%d\n",(int)1.99999999999);
>      printf("%d\n",(int)1.999999999999);
>      printf("%d\n",(int)1.9999999999999);
>      printf("%d\n",(int)1.99999999999999);
>      printf("%d\n",(int)1.999999999999999);
>      printf("%d\n",(int)1.9999999999999999);
>      printf("%d\n",(int)1.99999999999999999);
>      printf("%d\n",(int)1.999999999999999999);
>      printf("%d\n",(int)1.9999999999999999999);
> 
> from producing this output:
> 
>      1
>      1
>      1
>      1
>      1
>      1
>      1
>      2
>      2
>      2
>      2
> 
> or from similar results when you apply floor() to each constant.

Correct - and those results are allowed by the standard.

[toc] | [prev] | [next] | [standalone]


#139180

FromTim Rentsch <txr@alumni.caltech.edu>
Date2018-12-28 09:34 -0800
Message-ID<kfna7kpk11g.fsf@x-alumni2.alumni.caltech.edu>
In reply to#138982
Bart <bc@freeuk.com> writes:

> On 23/12/2018 17:56, Ben Bacarisse wrote:
>
>> Bart <bc@freeuk.com> writes:
>>
>>> How does "1.999999999999..." in source code end up as /exactly/ 2.0?
>>
>> Because the compiler is giving you the closest double to the number
>> you wrote.
>
> [...] What I don't know is the approach needed to turn a string of
> decimal digits into the closest possible ieee 64-bit float value.

Oh for goodness sakes.  You just wrote a multiple-precision
integer package.  Use it!

[toc] | [prev] | [next] | [standalone]


#139188

FromBart <bc@freeuk.com>
Date2018-12-28 20:29 +0000
Message-ID<AIvVD.134774$nd2.130504@fx22.am4>
In reply to#139180
On 28/12/2018 17:34, Tim Rentsch wrote:
> Bart <bc@freeuk.com> writes:
> 
>> On 23/12/2018 17:56, Ben Bacarisse wrote:
>>
>>> Bart <bc@freeuk.com> writes:
>>>
>>>> How does "1.999999999999..." in source code end up as /exactly/ 2.0?
>>>
>>> Because the compiler is giving you the closest double to the number
>>> you wrote.
>>
>> [...] What I don't know is the approach needed to turn a string of
>> decimal digits into the closest possible ieee 64-bit float value.
> 
> Oh for goodness sakes.  You just wrote a multiple-precision
> integer package.  Use it!
> 

I also wrote:

"What it doesn't explain is how a compiler ... armed only with the same 
64-bit set of binary floating point operations, actually achieves it."

I doubt whether most C compilers use extended precision (beyond 64 bits) 
to parse 64-bit float consts, so they must just use a different method.

(I would anyway consider an /arbitrary/ precision library to be overkill 
for such a purpose, and highly inefficient.)

If I run this program:

     #include <stdio.h>

     int main(void) {
         double x;

         x=1.9999999999999; printf("%016llX\n",*(long long int*)&x);
         x=1.999999999999999; printf("%016llX\n",*(long long int*)&x);
         x=1.9999999999999999; printf("%016llX\n",*(long long int*)&x);
         x=1.99999999999999999; printf("%016llX\n",*(long long int*)&x);
     }

Then the last 2 hex digits of the first two lines, before x becomes 2.0 
and the digits are zeros, are as follows:

   Pelles C      3F  FC
   lccwin        3E  FB
   DMC           3E  FB
   Tiny C        3E  FB
   MSVC          3E  FB
   gcc           3E  FB
   clang         3E  FB
   bcc (mine)    40  FD

So the consensus especially among the big three is 3E FB.

If I use strtod (using a temporary mock-up as the process requires 
re-building the string in the right format), I can also get 3E FB.

But if I simply tweak my code to do repeated multiplications rather than 
divisions (doing a single division at the end), then I can also get 3E 
FB that way. No extended precision needed, at least not for this example.

[toc] | [prev] | [next] | [standalone]


#139195

FromTim Rentsch <txr@alumni.caltech.edu>
Date2018-12-28 15:07 -0800
Message-ID<kfnwonti735.fsf@x-alumni2.alumni.caltech.edu>
In reply to#139188
Bart <bc@freeuk.com> writes:

> On 28/12/2018 17:34, Tim Rentsch wrote:
>
>> Bart <bc@freeuk.com> writes:
>>
>>> On 23/12/2018 17:56, Ben Bacarisse wrote:
>>>
>>>> Bart <bc@freeuk.com> writes:
>>>>
>>>>> How does "1.999999999999..." in source code end up as /exactly/ 2.0?
>>>>
>>>> Because the compiler is giving you the closest double to the number
>>>> you wrote.
>>>
>>> [...] What I don't know is the approach needed to turn a string of
>>> decimal digits into the closest possible ieee 64-bit float value.
>>
>> Oh for goodness sakes.  You just wrote a multiple-precision
>> integer package.  Use it!
>
> I also wrote:
>
> "What it doesn't explain is how a compiler ... armed only with the
> same 64-bit set of binary floating point operations, actually achieves
> it."
>
> I doubt whether most C compilers use extended precision (beyond 64
> bits) to parse 64-bit float consts, [...]

If they want correctly rounded results, they do.  This was
discussed recently in comp.arch.  To get correctly rounded
results for 64-bit floats needs a minimum on the order of 1000
bits.  (Disclaimer:  based on my memory and understanding of
reading the various articles referenced, both of which are
known to be imperfect.)

[toc] | [prev] | [next] | [standalone]


#139196

FromRichard Damon <Richard@Damon-Family.org>
Date2018-12-28 20:56 -0500
Message-ID<BvAVD.111139$BG6.54897@fx01.iad>
In reply to#139195
On 12/28/18 6:07 PM, Tim Rentsch wrote:
> Bart <bc@freeuk.com> writes:
> 
>> On 28/12/2018 17:34, Tim Rentsch wrote:
>>
>>> Bart <bc@freeuk.com> writes:
>>>
>>>> On 23/12/2018 17:56, Ben Bacarisse wrote:
>>>>
>>>>> Bart <bc@freeuk.com> writes:
>>>>>
>>>>>> How does "1.999999999999..." in source code end up as /exactly/ 2.0?
>>>>>
>>>>> Because the compiler is giving you the closest double to the number
>>>>> you wrote.
>>>>
>>>> [...] What I don't know is the approach needed to turn a string of
>>>> decimal digits into the closest possible ieee 64-bit float value.
>>>
>>> Oh for goodness sakes.  You just wrote a multiple-precision
>>> integer package.  Use it!
>>
>> I also wrote:
>>
>> "What it doesn't explain is how a compiler ... armed only with the
>> same 64-bit set of binary floating point operations, actually achieves
>> it."
>>
>> I doubt whether most C compilers use extended precision (beyond 64
>> bits) to parse 64-bit float consts, [...]
> 
> If they want correctly rounded results, they do.  This was
> discussed recently in comp.arch.  To get correctly rounded
> results for 64-bit floats needs a minimum on the order of 1000
> bits.  (Disclaimer:  based on my memory and understanding of
> reading the various articles referenced, both of which are
> known to be imperfect.)
> 

To get ABSOLUTELY perfect rounding, where the error is at most 0.50000
ULP, it can take that much to break the tie. If you are willing to relax
even a bit, to say 0.6 ULP, the needed precision drops drastically.
Since the standard only requires 1.5 ULP, my guess is it would be rare
to go to that level of effort for the conversion (unless you had other
needs for it).

[toc] | [prev] | [next] | [standalone]


#139212

FromTim Rentsch <txr@alumni.caltech.edu>
Date2018-12-29 09:22 -0800
Message-ID<kfnsgygfdsk.fsf@x-alumni2.alumni.caltech.edu>
In reply to#139196
Richard Damon <Richard@Damon-Family.org> writes:

> On 12/28/18 6:07 PM, Tim Rentsch wrote:
>
>> Bart <bc@freeuk.com> writes:
>>
>>> On 28/12/2018 17:34, Tim Rentsch wrote:
>>>
>>>> Bart <bc@freeuk.com> writes:
>>>>
>>>>> On 23/12/2018 17:56, Ben Bacarisse wrote:
>>>>>
>>>>>> Bart <bc@freeuk.com> writes:
>>>>>>
>>>>>>> How does "1.999999999999..." in source code end up as /exactly/ 2.0?
>>>>>>
>>>>>> Because the compiler is giving you the closest double to the number
>>>>>> you wrote.
>>>>>
>>>>> [...] What I don't know is the approach needed to turn a string of
>>>>> decimal digits into the closest possible ieee 64-bit float value.
>>>>
>>>> Oh for goodness sakes.  You just wrote a multiple-precision
>>>> integer package.  Use it!
>>>
>>> I also wrote:
>>>
>>> "What it doesn't explain is how a compiler ... armed only with the
>>> same 64-bit set of binary floating point operations, actually achieves
>>> it."
>>>
>>> I doubt whether most C compilers use extended precision (beyond 64
>>> bits) to parse 64-bit float consts, [...]
>>
>> If they want correctly rounded results, they do.  This was
>> discussed recently in comp.arch.  To get correctly rounded
>> results for 64-bit floats needs a minimum on the order of 1000
>> bits.  (Disclaimer:  based on my memory and understanding of
>> reading the various articles referenced, both of which are
>> known to be imperfect.)
>
> To get ABSOLUTELY perfect rounding, where the error is at most
> 0.50000 ULP, it can take that much to break the tie.  If you are
> willing to relax even a bit, to say 0.6 ULP, the needed precision
> drops drastically.  Since the standard only requires 1.5 ULP, my
> guess is it would be rare to go to that level of effort for the
> conversion (unless you had other needs for it).

Whatever other compilers might or might not do, Bart has all the
necessary support machinery readily at hand.  So for him it
should be easier to build a fully baked exact solution than to
fumble around trying to figure out how to build a half-baked
approximate solution (that works reliably in different floating
point environments).  Of course special casing could be added for
constants that have small numbers of digits and small exponents
(which presumably is most of the constants that will actually be
encountered), if one wanted to do that.  But reasoning about what
happens in floating point calculations is hard.  The amount of
intellectual effort needed to build an integer-based solution
should be much less.  Given that he has code to handle all the
integer calculations needed, that seems like an easier path to
follow.  Yes?

[toc] | [prev] | [next] | [standalone]


#139335

FromTim Rentsch <txr@alumni.caltech.edu>
Date2019-01-01 13:27 -0800
Message-ID<kfnk1jof4qx.fsf@x-alumni2.alumni.caltech.edu>
In reply to#139212
Tim Rentsch <txr@alumni.caltech.edu> writes:

> Richard Damon <Richard@Damon-Family.org> writes:
>
>> On 12/28/18 6:07 PM, Tim Rentsch wrote:
>>
>>> Bart <bc@freeuk.com> writes:
>>>
>>>> On 28/12/2018 17:34, Tim Rentsch wrote:
>>>>
>>>>> Bart <bc@freeuk.com> writes:
>>>>>
>>>>>> On 23/12/2018 17:56, Ben Bacarisse wrote:
>>>>>>
>>>>>>> Bart <bc@freeuk.com> writes:
>>>>>>>
>>>>>>>> How does "1.999999999999..." in source code end up as /exactly/ 2.0?
>>>>>>>
>>>>>>> Because the compiler is giving you the closest double to the number
>>>>>>> you wrote.
>>>>>>
>>>>>> [...] What I don't know is the approach needed to turn a string of
>>>>>> decimal digits into the closest possible ieee 64-bit float value.
>>>>>
>>>>> Oh for goodness sakes.  You just wrote a multiple-precision
>>>>> integer package.  Use it!
>>>>
>>>> I also wrote:
>>>>
>>>> "What it doesn't explain is how a compiler ... armed only with the
>>>> same 64-bit set of binary floating point operations, actually achieves
>>>> it."
>>>>
>>>> I doubt whether most C compilers use extended precision (beyond 64
>>>> bits) to parse 64-bit float consts, [...]
>>>
>>> If they want correctly rounded results, they do.  This was
>>> discussed recently in comp.arch.  To get correctly rounded
>>> results for 64-bit floats needs a minimum on the order of 1000
>>> bits.  (Disclaimer:  based on my memory and understanding of
>>> reading the various articles referenced, both of which are
>>> known to be imperfect.)
>>
>> To get ABSOLUTELY perfect rounding, where the error is at most
>> 0.50000 ULP, it can take that much to break the tie.  If you are
>> willing to relax even a bit, to say 0.6 ULP, the needed precision
>> drops drastically.  Since the standard only requires 1.5 ULP, my
>> guess is it would be rare to go to that level of effort for the
>> conversion (unless you had other needs for it).
>
> Whatever other compilers might or might not do, Bart has all the
> necessary support machinery readily at hand.  So for him it
> should be easier to build a fully baked exact solution than to
> fumble around trying to figure out how to build a half-baked
> approximate solution (that works reliably in different floating
> point environments).  Of course special casing could be added for
> constants that have small numbers of digits and small exponents
> (which presumably is most of the constants that will actually be
> encountered), if one wanted to do that.  But reasoning about what
> happens in floating point calculations is hard.  The amount of
> intellectual effort needed to build an integer-based solution
> should be much less.  Given that he has code to handle all the
> integer calculations needed, that seems like an easier path to
> follow.  Yes?

A short followup...

Mostly for my own education I knocked up an exactly rounding
floating-point constant conversion routine, completely from
scratch, no help from external libraries or anything of that
sort.  Time to implement was the better part of a morning,
resulting in about 250 lines of code of which function bodies
were 115 lines - average function length just over 6 lines,
ranging up to 19 lines maximum, with a median of 3 lines.

Disclosures: handles decimal constants with optional E-type
exponents, but not hex/binary style;  does not do range checking
or handle subnormals;  uses multiple precision arithmetic but not
arbitrary precision;  conversion is to 'double' although routines
for 'float' or 'long double' could use the same core routine and
would probably take about only 10 additional lines each.

[toc] | [prev] | [next] | [standalone]


#139348

FromBart <bc@freeuk.com>
Date2019-01-02 01:07 +0000
Message-ID<H9UWD.627706$gPr.533161@fx08.am4>
In reply to#139335
On 01/01/2019 21:27, Tim Rentsch wrote:
> Tim Rentsch <txr@alumni.caltech.edu> writes:

>> Whatever other compilers might or might not do, Bart has all the
>> necessary support machinery readily at hand.  So for him it
>> should be easier to build a fully baked exact solution than to
>> fumble around trying to figure out how to build a half-baked
>> approximate solution (that works reliably in different floating
>> point environments).  Of course special casing could be added for
>> constants that have small numbers of digits and small exponents
>> (which presumably is most of the constants that will actually be
>> encountered), if one wanted to do that.  But reasoning about what
>> happens in floating point calculations is hard.  The amount of
>> intellectual effort needed to build an integer-based solution
>> should be much less.  Given that he has code to handle all the
>> integer calculations needed, that seems like an easier path to
>> follow.  Yes?
> 
> A short followup...
> 
> Mostly for my own education I knocked up an exactly rounding
> floating-point constant conversion routine, completely from
> scratch, no help from external libraries or anything of that
> sort.  Time to implement was the better part of a morning,
> resulting in about 250 lines of code of which function bodies
> were 115 lines - average function length just over 6 lines,
> ranging up to 19 lines maximum, with a median of 3 lines.
> 
> Disclosures: handles decimal constants with optional E-type
> exponents, but not hex/binary style;  does not do range checking
> or handle subnormals;  uses multiple precision arithmetic but not
> arbitrary precision;  conversion is to 'double' although routines
> for 'float' or 'long double' could use the same core routine and
> would probably take about only 10 additional lines each.

I've since just made use of strtod() to do the conversion. Then I get 
results (for the binary reps of parsed decimal floats in source code) 
that much those of most other compilers including gcc.

I hadn't been much bothered: floating point arithmetic is generally only 
approximate, and a naive conversion from decimal to binary float might 
be out by up to a couple of least significant bits, out of 53. But it 
seemed important to match behaviour of other compilers.

(strtod() is supposed to also deal with hex input, but my main msvcrt C 
library doesn't support that (it will just return 0.0). Since I can't 
rely on strtod, that means taking care of hex directly, something I 
haven't yet done. But I think it will be easier thah decimal.)

[toc] | [prev] | [next] | [standalone]


Page 1 of 4  [1] 2 3 4  Next page →

Back to top | Article view | comp.lang.c


csiph-web