Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #138944 > unrolled thread
| Started by | chanrasjid@gmail.com |
|---|---|
| First post | 2018-12-23 03:02 -0800 |
| Last post | 2018-12-24 22:36 -0800 |
| Articles | 20 on this page of 62 — 17 participants |
Back to article view | Back to comp.lang.c
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 →
| From | chanrasjid@gmail.com |
|---|---|
| Date | 2018-12-23 03:02 -0800 |
| Subject | A 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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2018-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]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2018-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2018-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]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2018-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]
| From | chanrasjid@gmail.com |
|---|---|
| Date | 2018-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2018-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2018-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]
| From | James Kuyper <jameskuyper@alumni.caltech.edu> |
|---|---|
| Date | 2018-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]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2018-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2018-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]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2018-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]
| From | James Kuyper <jameskuyper@alumni.caltech.edu> |
|---|---|
| Date | 2018-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]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2018-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2018-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]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2018-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]
| From | Richard Damon <Richard@Damon-Family.org> |
|---|---|
| Date | 2018-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]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2018-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]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2019-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2019-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