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


Groups > comp.lang.c++ > #13973 > unrolled thread

C/C++ pitfalls related to 64-bits (unsigned long & double)

Started byAlex Vinokur <alex.vinokur@gmail.com>
First post2012-02-13 04:41 -0800
Last post2012-02-13 19:14 +0000
Articles 20 on this page of 67 — 17 participants

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


Contents

  C/C++ pitfalls related to 64-bits (unsigned long & double) Alex Vinokur <alex.vinokur@gmail.com> - 2012-02-13 04:41 -0800
    Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Leigh Johnston <leigh@i42.co.uk> - 2012-02-13 12:49 +0000
      Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Alex Vinokur <alex.vinokur@gmail.com> - 2012-02-13 04:59 -0800
        Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Leigh Johnston <leigh@i42.co.uk> - 2012-02-13 13:07 +0000
    Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Goran <goran.pusic@gmail.com> - 2012-02-13 04:54 -0800
      Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Alex Vinokur <alex.vinokur@gmail.com> - 2012-02-13 05:00 -0800
      Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Ben Bacarisse <ben.usenet@bsb.me.uk> - 2012-02-13 13:27 +0000
      Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Goran <goran.pusic@gmail.com> - 2012-02-13 07:14 -0800
    Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Eric Sosman <esosman@ieee-dot-org.invalid> - 2012-02-13 08:14 -0500
      Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Eric Sosman <esosman@ieee-dot-org.invalid> - 2012-02-13 08:17 -0500
        Re: C/C++ pitfalls related to 64-bits (unsigned long & double) glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2012-02-13 21:52 +0000
          Re: C/C++ pitfalls related to 64-bits (unsigned long & double) James Kuyper <jameskuyper@verizon.net> - 2012-02-13 17:08 -0500
          Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Eric Sosman <esosman@ieee-dot-org.invalid> - 2012-02-13 20:44 -0500
    Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Ben Bacarisse <ben.usenet@bsb.me.uk> - 2012-02-13 13:16 +0000
      Re: C/C++ pitfalls related to 64-bits (unsigned long & double) BGB <cr88192@hotmail.com> - 2012-02-13 11:18 -0700
        Re: C/C++ pitfalls related to 64-bits (unsigned long & double) "MikeWhy" <boat042-nospam@yahoo.com> - 2012-02-13 15:18 -0600
          Re: C/C++ pitfalls related to 64-bits (unsigned long & double) James Kuyper <jameskuyper@verizon.net> - 2012-02-13 16:26 -0500
            Re: C/C++ pitfalls related to 64-bits (unsigned long & double) "MikeWhy" <boat042-nospam@yahoo.com> - 2012-02-13 16:05 -0600
              Re: C/C++ pitfalls related to 64-bits (unsigned long & double) James Kuyper <jameskuyper@verizon.net> - 2012-02-13 17:13 -0500
                Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Ben Bacarisse <ben.usenet@bsb.me.uk> - 2012-02-13 23:35 +0000
                  Re: C/C++ pitfalls related to 64-bits (unsigned long & double) BGB <cr88192@hotmail.com> - 2012-02-13 17:25 -0700
                    Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Ben Bacarisse <ben.usenet@bsb.me.uk> - 2012-02-14 01:45 +0000
                      Re: C/C++ pitfalls related to 64-bits (unsigned long & double) BGB <cr88192@hotmail.com> - 2012-02-13 18:55 -0700
                        Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Miles Bader <miles@gnu.org> - 2012-02-14 11:51 +0900
                          Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Ben Bacarisse <ben.usenet@bsb.me.uk> - 2012-02-14 03:13 +0000
                          Re: C/C++ pitfalls related to 64-bits (unsigned long & double) BGB <cr88192@hotmail.com> - 2012-02-13 20:39 -0700
                            Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Miles Bader <miles@gnu.org> - 2012-02-14 13:25 +0900
                              Re: C/C++ pitfalls related to 64-bits (unsigned long & double) BGB <cr88192@hotmail.com> - 2012-02-13 23:22 -0700
                                Re: C/C++ pitfalls related to 64-bits (unsigned long & double) "MikeWhy" <boat042-nospam@yahoo.com> - 2012-02-14 02:56 -0600
                                  Re: C/C++ pitfalls related to 64-bits (unsigned long & double) BGB <cr88192@hotmail.com> - 2012-02-14 09:13 -0700
                                    Re: C/C++ pitfalls related to 64-bits (unsigned long & double) "MikeWhy" <boat042-nospam@yahoo.com> - 2012-02-14 11:16 -0600
                                      Re: C/C++ pitfalls related to 64-bits (unsigned long & double) BGB <cr88192@hotmail.com> - 2012-02-14 12:27 -0700
                          Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Eric Sosman <esosman@ieee-dot-org.invalid> - 2012-02-13 23:04 -0500
                            Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Miles Bader <miles@gnu.org> - 2012-02-14 14:00 +0900
                              Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Robert Wessel <robertwessel2@yahoo.com> - 2012-02-14 00:45 -0600
                                Re: C/C++ pitfalls related to 64-bits (unsigned long & double) glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2012-02-14 14:22 +0000
                                  Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Robert Wessel <robertwessel2@yahoo.com> - 2012-02-14 13:06 -0600
                                    Re: C/C++ pitfalls related to 64-bits (unsigned long & double) BGB <cr88192@hotmail.com> - 2012-02-14 12:40 -0700
                          Re: C/C++ pitfalls related to 64-bits (unsigned long & double) James Kuyper <jameskuyper@verizon.net> - 2012-02-13 23:05 -0500
                            Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Miles Bader <miles@gnu.org> - 2012-02-14 14:01 +0900
                      Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Keith Thompson <kst-u@mib.org> - 2012-02-14 03:01 -0800
              Re: C/C++ pitfalls related to 64-bits (unsigned long & double) jgk@panix.com (Joe keane) - 2012-02-14 20:45 +0000
                Re: C/C++ pitfalls related to 64-bits (unsigned long & double) glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2012-02-14 22:57 +0000
                Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Keith Thompson <kst-u@mib.org> - 2012-02-14 15:38 -0800
                  Re: C/C++ pitfalls related to 64-bits (unsigned long & double) glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2012-02-15 00:22 +0000
                    Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Robert Wessel <robertwessel2@yahoo.com> - 2012-02-15 00:39 -0600
                      Re: C/C++ pitfalls related to 64-bits (unsigned long & double) glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2012-02-15 08:00 +0000
        Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Ben Bacarisse <ben.usenet@bsb.me.uk> - 2012-02-13 22:00 +0000
          Re: C/C++ pitfalls related to 64-bits (unsigned long & double) BGB <cr88192@hotmail.com> - 2012-02-13 17:04 -0700
            Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Malcolm McLean <malcolm.mclean5@btinternet.com> - 2012-02-13 16:15 -0800
            Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Ben Bacarisse <ben.usenet@bsb.me.uk> - 2012-02-14 00:46 +0000
              Re: C/C++ pitfalls related to 64-bits (unsigned long & double) BGB <cr88192@hotmail.com> - 2012-02-13 18:31 -0700
                Re: C/C++ pitfalls related to 64-bits (unsigned long & double) BGB <cr88192@hotmail.com> - 2012-02-13 18:41 -0700
                  Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Ben Bacarisse <ben.usenet@bsb.me.uk> - 2012-02-14 02:33 +0000
                Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Robert Wessel <robertwessel2@yahoo.com> - 2012-02-14 00:24 -0600
                  Re: C/C++ pitfalls related to 64-bits (unsigned long & double) BGB <cr88192@hotmail.com> - 2012-02-13 23:42 -0700
                  Re: C/C++ pitfalls related to 64-bits (unsigned long & double) James Kuyper <jameskuyper@verizon.net> - 2012-02-14 06:48 -0500
                    Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Ben Bacarisse <ben.usenet@bsb.me.uk> - 2012-02-14 13:51 +0000
                    Re: C/C++ pitfalls related to 64-bits (unsigned long & double) BGB <cr88192@hotmail.com> - 2012-02-14 08:42 -0700
                      Re: C/C++ pitfalls related to 64-bits (unsigned long & double) glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2012-02-14 19:58 +0000
                  Re: C/C++ pitfalls related to 64-bits (unsigned long & double) glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2012-02-14 14:27 +0000
                    Re: C/C++ pitfalls related to 64-bits (unsigned long & double) BGB <cr88192@hotmail.com> - 2012-02-14 09:10 -0700
                      Re: C/C++ pitfalls related to 64-bits (unsigned long & double) glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2012-02-14 20:02 +0000
    Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Noob <root@127.0.0.1> - 2012-02-13 15:22 +0100
    Re: C/C++ pitfalls related to 64-bits (unsigned long & double) legalize+jeeves@mail.xmission.com (Richard) - 2012-02-13 18:49 +0000
      Re: C/C++ pitfalls related to 64-bits (unsigned long & double) James Kuyper <jameskuyper@verizon.net> - 2012-02-13 13:55 -0500
        Re: C/C++ pitfalls related to 64-bits (unsigned long & double) Juha Nieminen <nospam@thanks.invalid> - 2012-02-13 19:14 +0000

Page 3 of 4 — ← Prev page 1 2 [3] 4  Next page →


#14033

FromKeith Thompson <kst-u@mib.org>
Date2012-02-14 03:01 -0800
Message-ID<lnd39hisqp.fsf@nuthaus.mib.org>
In reply to#14011
Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
> BGB <cr88192@hotmail.com> writes:
[...]
>> as I understand it, the entire range of 32 bit integers can be exactly
>> represented by a double.
>
> In the architecture in question, yes.
[...]

In any conforming C implementation; the requirements on double are such
that it can represent any 32-bit integer value exactly.

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
    Will write code for food.
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

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


#14056

Fromjgk@panix.com (Joe keane)
Date2012-02-14 20:45 +0000
Message-ID<jheh5p$s7h$1@reader1.panix.com>
In reply to#13999
In article <jhc1g6$afj$1@dont-email.me>,
MikeWhy <boat042-nospam@yahoo.com> wrote: 
>Still wondering here about the "up for grabs" part. It seems to imply
>some edge condition that isn't accounted for.

a)

machine has internal format that is superset of 'long' and 'double'

b)

compiler notice the code does
	 long -> double -> long
and say 'screw that, don't do nothing'[1]

[1] in general, people don't get mad if you give -more- precision than
that required, just supply a 'yes i'm a masochist' option

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


#14058

Fromglen herrmannsfeldt <gah@ugcs.caltech.edu>
Date2012-02-14 22:57 +0000
Message-ID<jheosg$qa6$1@speranza.aioe.org>
In reply to#14056
In comp.lang.c++ Joe keane <jgk@panix.com> wrote:

(snip)
> a) machine has internal format that is superset of 'long' and 'double'

> b) compiler notice the code does:  long -> double -> long
> and say 'screw that, don't do nothing'[1]

> [1] in general, people don't get mad if you give -more- precision than
> that required, just supply a 'yes i'm a masochist' option

gcc has the --float-store option, requiring it to store intermediate
values and refetch them (hopefully in cache) to avoid the excess
precision problem. I don't know if that also applied to compile time
expression evaluation, though.

There are some algorithms that require a consistent precision,
and that will fail if sometimes given more.

-- glen

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


#14060

FromKeith Thompson <kst-u@mib.org>
Date2012-02-14 15:38 -0800
Message-ID<ln7gzphtpa.fsf@nuthaus.mib.org>
In reply to#14056
jgk@panix.com (Joe keane) writes:
[...]
> [1] in general, people don't get mad if you give -more- precision than
> that required, just supply a 'yes i'm a masochist' option

People will sometimes get quite mad if you give them *inconsistent*
precision.

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
    Will write code for food.
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

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


#14061

Fromglen herrmannsfeldt <gah@ugcs.caltech.edu>
Date2012-02-15 00:22 +0000
Message-ID<jhetra$4b9$2@speranza.aioe.org>
In reply to#14060
In comp.lang.c++ Keith Thompson <kst-u@mib.org> wrote:
> jgk@panix.com (Joe keane) writes:

>> [1] in general, people don't get mad if you give -more- precision than
>> that required, just supply a 'yes i'm a masochist' option

> People will sometimes get quite mad if you give them *inconsistent*
> precision.

Well, for the long story, when the 8087 was developed, the idea
was for an infinite register stack. When it was full, there
would be an interrupt, some would be spilled to memory, and
then continue on. The chip was designed and built before anyone
tried to write the software to do it, when it was found
not to be possible.

Why it wasn't fixed for the 80287 and 80837, I don't know.

That would, at least, have allowed for intermediate values
to always have extra precision. 

Now, when you add optimizations, such as common subexpression
elimination, and keep values in registers between statements,
then again you have inconsistent precision. The infinite
stack would have allowed for more consistency.

As an example of what is possible, without actually seeing
a compiler do it, if you:

  X=A*B+C*D;
  Y=A*B-C*D;

The compiler might keep C*D with extra precision, but not A*B.

Now, if A=C and B=D, Y should be zero but it isn't.

With an infinite stack, one might hope that the intermediates
for A*B and C*D had the same precision, extra or not.

-- glen

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


#14064

FromRobert Wessel <robertwessel2@yahoo.com>
Date2012-02-15 00:39 -0600
Message-ID<88kmj714tu9itfd9lbtvj4m8upuf8t6uge@4ax.com>
In reply to#14061
On Wed, 15 Feb 2012 00:22:03 +0000 (UTC), glen herrmannsfeldt
<gah@ugcs.caltech.edu> wrote:

>In comp.lang.c++ Keith Thompson <kst-u@mib.org> wrote:
>> jgk@panix.com (Joe keane) writes:
>
>>> [1] in general, people don't get mad if you give -more- precision than
>>> that required, just supply a 'yes i'm a masochist' option
>
>> People will sometimes get quite mad if you give them *inconsistent*
>> precision.
>
>Well, for the long story, when the 8087 was developed, the idea
>was for an infinite register stack. When it was full, there
>would be an interrupt, some would be spilled to memory, and
>then continue on. The chip was designed and built before anyone
>tried to write the software to do it, when it was found
>not to be possible.
>
>Why it wasn't fixed for the 80287 and 80837, I don't know.
>
>That would, at least, have allowed for intermediate values
>to always have extra precision. 


I believe the original plan was to do the spills in hardware, but they
didn't have the die space, so they decided to punt to software.  But
as you said, they never tried doing it until it was too late.  The
problems center around being able to handle multiple exceptional
conditions at once - basically you can't quite figure out the exact
state of the 8087 at the point where it generates the stack overflow.
Once the interrupt happens, several possible exceptions have gotten
merged together.

And no, it was never fixed.

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


#14066

Fromglen herrmannsfeldt <gah@ugcs.caltech.edu>
Date2012-02-15 08:00 +0000
Message-ID<jhfomd$qgp$1@speranza.aioe.org>
In reply to#14064
In comp.lang.c++ Robert Wessel <robertwessel2@yahoo.com> wrote:

(snip on 8087 stack overflow detection)

> I believe the original plan was to do the spills in hardware, but they
> didn't have the die space, so they decided to punt to software.  But
> as you said, they never tried doing it until it was too late.  The
> problems center around being able to handle multiple exceptional
> conditions at once - basically you can't quite figure out the exact
> state of the 8087 at the point where it generates the stack overflow.
> Once the interrupt happens, several possible exceptions have gotten
> merged together.

> And no, it was never fixed.

Multiple imprecise interrupts. The IBM 360/91 allows for out of
order execution. If a program interrupt occurs, it has to finish
all instructions that are in the pipeline, possibly generating
more exceptions. Also, the PSW might be nowhere near the actual
cause. Probably too long ago for C++, though. I know one was
running at least until 1981.

This was 'fixed' in S/370, as it is hard to do virtual
storage if you don't know where the page fault occurred.

-- glen

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


#13998

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2012-02-13 22:00 +0000
Message-ID<0.3ea307f9a62a254551d3.20120213220013GMT.87k43q5r82.fsf@bsb.me.uk>
In reply to#13989
BGB <cr88192@hotmail.com> writes:
<snip>
> also, even though double has more bits than, say, an integer, does not
> mean it will reliably encode an integer's value (it can do so in
> theory, and will most often do so, but whether or not it will actually
> always do so is more "up for grabs").

It's not up for grabs in C (and C++ is essentially the same in this
regard).  If the integer can be represented exactly in the floating
point type, it must be.

<snip>
> float or double to int, will generally truncate the value, yielding
> the integer representation as rounded towards 0.

That's true of the truncated value can be represented as an int.  If
not, the behaviour is undefined.  For example, in the example that
triggered this thread my implementation produces zero as the result.

> the result then is a tendency for an int->double->int conversion to
> have a small chance to drop the integer value towards 0 (why? I don't
> know exactly, but I have observed it before).

If the int is "in range" you don't have a conforming C implementation.

> one can counteract this by fudging the value with a small epsilon
> prior to converting back into an integer.
>
> say, for example (untested, from memory):
> (v>=0)?((int)(v+0.0001)):((int)(v-0.0001));

I can't see how this helps.  If v is representable exactly as a double,
the round trip has no effect so this code is not needed. Can you give me
a use-case?

> can't say it will always work, but similar seems to work fairly well
> IME (at least on generic x86 based targets).

-- 
Ben.

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


#14003

FromBGB <cr88192@hotmail.com>
Date2012-02-13 17:04 -0700
Message-ID<jhc8fr$6sk$1@news.albasani.net>
In reply to#13998
On 2/13/2012 3:00 PM, Ben Bacarisse wrote:
> BGB<cr88192@hotmail.com>  writes:
> <snip>
>> also, even though double has more bits than, say, an integer, does not
>> mean it will reliably encode an integer's value (it can do so in
>> theory, and will most often do so, but whether or not it will actually
>> always do so is more "up for grabs").
>
> It's not up for grabs in C (and C++ is essentially the same in this
> regard).  If the integer can be represented exactly in the floating
> point type, it must be.
>

theoretically, yes.

given a double has 52 bits, and int is 32 bits, and it is possible to 
convert exactly, it should always be reliable.


but, I have seen it not work exactly right, albeit in rare cases (IME, 
usually on 64-bit Linux systems, generally fairly rare and when using 
AMD chips IME).

I haven't seen the issue on Win64 though that I can remember, nor with 
32-bit code, so I don't know.

it "might" have something to do with SSE for all I know (since 32-bit 
code typically uses x87, and 64-bit typically uses SSE), or maybe 
something to do with GCC, or similar.

might require researching, like trying to figure how exactly AMD chips 
implement the "cvtsi2sd" and "cvtsd2si" instructions or similar... (I am 
not even particularly sure which side of the conversion would have been 
introducing a loss of accuracy, or if the cause could be something else 
"in the middle" somewhere).


> <snip>
>> float or double to int, will generally truncate the value, yielding
>> the integer representation as rounded towards 0.
>
> That's true of the truncated value can be represented as an int.  If
> not, the behaviour is undefined.  For example, in the example that
> triggered this thread my implementation produces zero as the result.
>

I didn't notice that part of the thread until after I posted.


>> the result then is a tendency for an int->double->int conversion to
>> have a small chance to drop the integer value towards 0 (why? I don't
>> know exactly, but I have observed it before).
>
> If the int is "in range" you don't have a conforming C implementation.
>

could be.


all I know is I am fairly sure I have seen it happen in the past (unless 
I remember seeing an issue probably with other causes, like maybe 
arithmetic was being done somewhere was messing it up or similar).

I never really went and did an in depth investigation of the problem, as 
it was fixed easily enough when I ran into it (and is not too much 
different than other epsilon-type stuff when working with floating-point 
types).

like, the whole: "if(fabs(a-b)<0.000001)" thing to compare for equality 
or similar.


>> one can counteract this by fudging the value with a small epsilon
>> prior to converting back into an integer.
>>
>> say, for example (untested, from memory):
>> (v>=0)?((int)(v+0.0001)):((int)(v-0.0001));
>
> I can't see how this helps.  If v is representable exactly as a double,
> the round trip has no effect so this code is not needed. Can you give me
> a use-case?
>

generally, I had seen it in my 3D engine, where in some cases integers 
ended up getting converted to doubles and back, and sometimes they would 
get "bumped" in this way. adding a small adjustment seemed to fix the 
problem.

mostly since then I have been working under the assumption of trying to 
avoid conversions to/from floating point types when possible (partly 
also as I had made the past observation that these conversions have also 
tended to be costly).


or such...

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


#14004

FromMalcolm McLean <malcolm.mclean5@btinternet.com>
Date2012-02-13 16:15 -0800
Message-ID<c040af34-610a-42c5-85ff-f58b0165cede@dq9g2000vbb.googlegroups.com>
In reply to#14003
On Feb 14, 12:04 am, BGB <cr88...@hotmail.com> wrote:
> On 2/13/2012 3:00 PM, Ben Bacarisse wrote:
> > BGB<cr88...@hotmail.com>  writes:
>
> >> the result then is a tendency for an int->double->int conversion to
> >> have a small chance to drop the integer value towards 0 (why? I don't
> >> know exactly, but I have observed it before).
>
> > If the int is "in range" you don't have a conforming C implementation.
>
> all I know is I am fairly sure I have seen it happen in the past (unless
> I remember seeing an issue probably with other causes, like maybe
> arithmetic was being done somewhere was messing it up or similar).
>
Sounds like a hardware bug. The C compiler can't necessarily work
round those.
--
C programming material on my website
http://www.malcolmmclean.site11.com/www

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


#14007

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2012-02-14 00:46 +0000
Message-ID<0.a4dc5cc322c68708dbd6.20120214004648GMT.87ehty44xz.fsf@bsb.me.uk>
In reply to#14003
BGB <cr88192@hotmail.com> writes:

> On 2/13/2012 3:00 PM, Ben Bacarisse wrote:
>> BGB<cr88192@hotmail.com>  writes:
>> <snip>
>>> also, even though double has more bits than, say, an integer, does not
>>> mean it will reliably encode an integer's value (it can do so in
>>> theory, and will most often do so, but whether or not it will actually
>>> always do so is more "up for grabs").
>>
>> It's not up for grabs in C (and C++ is essentially the same in this
>> regard).  If the integer can be represented exactly in the floating
>> point type, it must be.
>>
>
> theoretically, yes.
>
> given a double has 52 bits, and int is 32 bits, and it is possible to
> convert exactly, it should always be reliable.
>
> but, I have seen it not work exactly right, albeit in rare cases (IME,
> usually on 64-bit Linux systems, generally fairly rare and when using
> AMD chips IME).
>
> I haven't seen the issue on Win64 though that I can remember, nor with
> 32-bit code, so I don't know.
>
> it "might" have something to do with SSE for all I know (since 32-bit
> code typically uses x87, and 64-bit typically uses SSE), or maybe
> something to do with GCC, or similar.
>
> might require researching, like trying to figure how exactly AMD chips
> implement the "cvtsi2sd" and "cvtsd2si" instructions or similar... (I
> am not even particularly sure which side of the conversion would have
> been introducing a loss of accuracy, or if the cause could be
> something else "in the middle" somewhere).

Since this behaviour is required for C implementations to be conforming,
deviation from it is important.  Was there perhaps a bug report filed?

<snip>
>>> one can counteract this by fudging the value with a small epsilon
>>> prior to converting back into an integer.
>>>
>>> say, for example (untested, from memory):
>>> (v>=0)?((int)(v+0.0001)):((int)(v-0.0001));
>>
>> I can't see how this helps.  If v is representable exactly as a double,
>> the round trip has no effect so this code is not needed. Can you give me
>> a use-case?
>>
>
> generally, I had seen it in my 3D engine, where in some cases integers
> ended up getting converted to doubles and back, and sometimes they
> would get "bumped" in this way. adding a small adjustment seemed to
> fix the problem.

Can you add a test to the code to print v when

 (int)(double)v != v &&
 (v >= 0 ? (int)(v+0.0001) : (int)(v-0.0001)) == v

?  That way we might get an example of the problem you are reporting.

<snip>
-- 
Ben.

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


#14008

FromBGB <cr88192@hotmail.com>
Date2012-02-13 18:31 -0700
Message-ID<jhcdk8$4h8$1@news.albasani.net>
In reply to#14007
On 2/13/2012 5:46 PM, Ben Bacarisse wrote:
> BGB<cr88192@hotmail.com>  writes:
>
>> On 2/13/2012 3:00 PM, Ben Bacarisse wrote:
>>> BGB<cr88192@hotmail.com>   writes:
>>> <snip>
>>>> also, even though double has more bits than, say, an integer, does not
>>>> mean it will reliably encode an integer's value (it can do so in
>>>> theory, and will most often do so, but whether or not it will actually
>>>> always do so is more "up for grabs").
>>>
>>> It's not up for grabs in C (and C++ is essentially the same in this
>>> regard).  If the integer can be represented exactly in the floating
>>> point type, it must be.
>>>
>>
>> theoretically, yes.
>>
>> given a double has 52 bits, and int is 32 bits, and it is possible to
>> convert exactly, it should always be reliable.
>>
>> but, I have seen it not work exactly right, albeit in rare cases (IME,
>> usually on 64-bit Linux systems, generally fairly rare and when using
>> AMD chips IME).
>>
>> I haven't seen the issue on Win64 though that I can remember, nor with
>> 32-bit code, so I don't know.
>>
>> it "might" have something to do with SSE for all I know (since 32-bit
>> code typically uses x87, and 64-bit typically uses SSE), or maybe
>> something to do with GCC, or similar.
>>
>> might require researching, like trying to figure how exactly AMD chips
>> implement the "cvtsi2sd" and "cvtsd2si" instructions or similar... (I
>> am not even particularly sure which side of the conversion would have
>> been introducing a loss of accuracy, or if the cause could be
>> something else "in the middle" somewhere).
>
> Since this behaviour is required for C implementations to be conforming,
> deviation from it is important.  Was there perhaps a bug report filed?
>

not at the time, I merely thought of it as an interesting occurrence and 
worked around it.


> <snip>
>>>> one can counteract this by fudging the value with a small epsilon
>>>> prior to converting back into an integer.
>>>>
>>>> say, for example (untested, from memory):
>>>> (v>=0)?((int)(v+0.0001)):((int)(v-0.0001));
>>>
>>> I can't see how this helps.  If v is representable exactly as a double,
>>> the round trip has no effect so this code is not needed. Can you give me
>>> a use-case?
>>>
>>
>> generally, I had seen it in my 3D engine, where in some cases integers
>> ended up getting converted to doubles and back, and sometimes they
>> would get "bumped" in this way. adding a small adjustment seemed to
>> fix the problem.
>
> Can you add a test to the code to print v when
>
>   (int)(double)v != v&&
>   (v>= 0 ? (int)(v+0.0001) : (int)(v-0.0001)) == v
>
> ?  That way we might get an example of the problem you are reporting.
>

I would have to find an example of it again...

I remember seeing the problem a few years ago in some code of mine, but 
don't have any recent memory of bugs resulting from it (but, then again, 
this could also be due to code paranoia...).

just went and tried to recreate it, with mixed results:
a raw conversion does not show any issues (seems to always be reliable);
if I add a value to the double, and subtract the same value, then it 
starts acting up.

testing the code below in Fedora 13 x86-64 within VMware (yes, not the 
raw HW, but I would otherwise have to reboot).

#include <stdio.h>

int main()
{
	double d;
	int i, j, k;

	for(i=0; i<100000000; i++)
	{
		j=rand()*rand()*i;
		d=j;
		d=d+1.0; //(1)
		d=d-1.0; //(1)
		k=d; //(2)
		k=(d>=0?(int)(d+0.0001):(int)(d-0.0001)); //(2)
		if(j!=k)
			printf("%d %d\n", j, k);
	}
}

1: if these lines are commented out, then the printf is never called, 
but if uncommented (along with using different constant values), then I 
start seeing messages (with it off-by-one, rounded towards 0).

2: if I switch to the second form, which makes the fudging, then the 
messages disappear (they still appear with the first form).

so, it would seem to be mostly an issue in this case of whether or not 
one does any arithmetic on the values (not sure whether or not this 
still counts). CPU is an "AMD Athlon II X4 630".

or, at least, this is what I am seeing here...


here is the inner part of the loop (in ASM):
.L4:
	movl	$0, %eax
	call	rand
	movl	%eax, %ebx
	movl	$0, %eax
	call	rand
	imull	%ebx, %eax
	imull	-20(%rbp), %eax
	movl	%eax, -24(%rbp)
	cvtsi2sd	-24(%rbp), %xmm0
	movsd	%xmm0, -32(%rbp)
	movsd	-32(%rbp), %xmm1
	movsd	.LC0(%rip), %xmm0
	addsd	%xmm1, %xmm0
	movsd	%xmm0, -32(%rbp)
	movsd	-32(%rbp), %xmm0
	movsd	.LC0(%rip), %xmm1
	subsd	%xmm1, %xmm0
	movsd	%xmm0, -32(%rbp)
	movsd	-32(%rbp), %xmm0
	cvttsd2si	%xmm0, %eax
	movl	%eax, -36(%rbp)
	movl	-24(%rbp), %eax
	cmpl	-36(%rbp), %eax
	je	.L3
	movl	$.LC1, %eax
	movl	-36(%rbp), %edx
	movl	-24(%rbp), %ecx
	movl	%ecx, %esi
	movq	%rax, %rdi
	movl	$0, %eax
	call	printf
.L3:

...

.LC0:
	.long	0
	.long	1072693248

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


#14009

FromBGB <cr88192@hotmail.com>
Date2012-02-13 18:41 -0700
Message-ID<jhce68$59t$1@news.albasani.net>
In reply to#14008
On 2/13/2012 6:31 PM, BGB wrote:
> On 2/13/2012 5:46 PM, Ben Bacarisse wrote:
>> BGB<cr88192@hotmail.com> writes:
>>
>>> On 2/13/2012 3:00 PM, Ben Bacarisse wrote:
>>>> BGB<cr88192@hotmail.com> writes:
>>>> <snip>
>>>>> also, even though double has more bits than, say, an integer, does not
>>>>> mean it will reliably encode an integer's value (it can do so in
>>>>> theory, and will most often do so, but whether or not it will actually
>>>>> always do so is more "up for grabs").
>>>>
>>>> It's not up for grabs in C (and C++ is essentially the same in this
>>>> regard). If the integer can be represented exactly in the floating
>>>> point type, it must be.
>>>>
>>>
>>> theoretically, yes.
>>>
>>> given a double has 52 bits, and int is 32 bits, and it is possible to
>>> convert exactly, it should always be reliable.
>>>
>>> but, I have seen it not work exactly right, albeit in rare cases (IME,
>>> usually on 64-bit Linux systems, generally fairly rare and when using
>>> AMD chips IME).
>>>
>>> I haven't seen the issue on Win64 though that I can remember, nor with
>>> 32-bit code, so I don't know.
>>>
>>> it "might" have something to do with SSE for all I know (since 32-bit
>>> code typically uses x87, and 64-bit typically uses SSE), or maybe
>>> something to do with GCC, or similar.
>>>
>>> might require researching, like trying to figure how exactly AMD chips
>>> implement the "cvtsi2sd" and "cvtsd2si" instructions or similar... (I
>>> am not even particularly sure which side of the conversion would have
>>> been introducing a loss of accuracy, or if the cause could be
>>> something else "in the middle" somewhere).
>>
>> Since this behaviour is required for C implementations to be conforming,
>> deviation from it is important. Was there perhaps a bug report filed?
>>
>
> not at the time, I merely thought of it as an interesting occurrence and
> worked around it.
>
>
>> <snip>
>>>>> one can counteract this by fudging the value with a small epsilon
>>>>> prior to converting back into an integer.
>>>>>
>>>>> say, for example (untested, from memory):
>>>>> (v>=0)?((int)(v+0.0001)):((int)(v-0.0001));
>>>>
>>>> I can't see how this helps. If v is representable exactly as a double,
>>>> the round trip has no effect so this code is not needed. Can you
>>>> give me
>>>> a use-case?
>>>>
>>>
>>> generally, I had seen it in my 3D engine, where in some cases integers
>>> ended up getting converted to doubles and back, and sometimes they
>>> would get "bumped" in this way. adding a small adjustment seemed to
>>> fix the problem.
>>
>> Can you add a test to the code to print v when
>>
>> (int)(double)v != v&&
>> (v>= 0 ? (int)(v+0.0001) : (int)(v-0.0001)) == v
>>
>> ? That way we might get an example of the problem you are reporting.
>>
>
> I would have to find an example of it again...
>
> I remember seeing the problem a few years ago in some code of mine, but
> don't have any recent memory of bugs resulting from it (but, then again,
> this could also be due to code paranoia...).
>
> just went and tried to recreate it, with mixed results:
> a raw conversion does not show any issues (seems to always be reliable);
> if I add a value to the double, and subtract the same value, then it
> starts acting up.
>
> testing the code below in Fedora 13 x86-64 within VMware (yes, not the
> raw HW, but I would otherwise have to reboot).
>
> #include <stdio.h>
>
> int main()
> {
> double d;
> int i, j, k;
>
> for(i=0; i<100000000; i++)
> {
> j=rand()*rand()*i;
> d=j;
> d=d+1.0; //(1)
> d=d-1.0; //(1)
> k=d; //(2)
> k=(d>=0?(int)(d+0.0001):(int)(d-0.0001)); //(2)
> if(j!=k)
> printf("%d %d\n", j, k);
> }
> }
>
> 1: if these lines are commented out, then the printf is never called,
> but if uncommented (along with using different constant values), then I
> start seeing messages (with it off-by-one, rounded towards 0).
>
> 2: if I switch to the second form, which makes the fudging, then the
> messages disappear (they still appear with the first form).
>
> so, it would seem to be mostly an issue in this case of whether or not
> one does any arithmetic on the values (not sure whether or not this
> still counts). CPU is an "AMD Athlon II X4 630".
>
> or, at least, this is what I am seeing here...
>

oh yeah, here is an example of the output (with a slight tweak to show 
the value held by the double):
1073741824 1073741823 41CFFFFFFFFFFFFF
1073741824 1073741823 41CFFFFFFFFFFFFF
262144 262143 410FFFFFFFFFFFFF
262144 262143 410FFFFFFFFFFFFF
262144 262143 410FFFFFFFFFFFFF
67108864 67108863 418FFFFFFFFFFFFF
4 3 400FFFFFFFFFFFFF
1073741824 1073741823 41CFFFFFFFFFFFFF
1073741824 1073741823 41CFFFFFFFFFFFFF
1073741824 1073741823 41CFFFFFFFFFFFFF
4194304 4194303 414FFFFFFFFFFFFF
4194304 4194303 414FFFFFFFFFFFFF
16384 16383 40CFFFFFFFFFFFFF
67108864 67108863 418FFFFFFFFFFFFF
16384 16383 40CFFFFFFFFFFFFF

hmm, a lot of the same values seem to repeat...


>
> here is the inner part of the loop (in ASM):
> .L4:
> movl $0, %eax
> call rand
> movl %eax, %ebx
> movl $0, %eax
> call rand
> imull %ebx, %eax
> imull -20(%rbp), %eax
> movl %eax, -24(%rbp)
> cvtsi2sd -24(%rbp), %xmm0
> movsd %xmm0, -32(%rbp)
> movsd -32(%rbp), %xmm1
> movsd .LC0(%rip), %xmm0
> addsd %xmm1, %xmm0
> movsd %xmm0, -32(%rbp)
> movsd -32(%rbp), %xmm0
> movsd .LC0(%rip), %xmm1
> subsd %xmm1, %xmm0
> movsd %xmm0, -32(%rbp)
> movsd -32(%rbp), %xmm0
> cvttsd2si %xmm0, %eax
> movl %eax, -36(%rbp)
> movl -24(%rbp), %eax
> cmpl -36(%rbp), %eax
> je .L3
> movl $.LC1, %eax
> movl -36(%rbp), %edx
> movl -24(%rbp), %ecx
> movl %ecx, %esi
> movq %rax, %rdi
> movl $0, %eax
> call printf
> .L3:
>
> ...
>
> .LC0:
> .long 0
> .long 1072693248

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


#14013

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2012-02-14 02:33 +0000
Message-ID<0.7532f15ffa230ced061e.20120214023319GMT.87wr7q2lg0.fsf@bsb.me.uk>
In reply to#14009
I've set followup-to: since the code is all C.

BGB <cr88192@hotmail.com> writes:

> On 2/13/2012 6:31 PM, BGB wrote:
<snip>
>> just went and tried to recreate it, with mixed results:
>> a raw conversion does not show any issues (seems to always be reliable);
>> if I add a value to the double, and subtract the same value, then it
>> starts acting up.

Yes, it looks like conversion is not the issue.

>> testing the code below in Fedora 13 x86-64 within VMware (yes, not the
>> raw HW, but I would otherwise have to reboot).
>>
>> #include <stdio.h>
>>
>> int main()
>> {
>> double d;
>> int i, j, k;
>>
>> for(i=0; i<100000000; i++)
>> {
>> j=rand()*rand()*i;
>> d=j;
>> d=d+1.0; //(1)
>> d=d-1.0; //(1)
>> k=d; //(2)
>> k=(d>=0?(int)(d+0.0001):(int)(d-0.0001)); //(2)
>> if(j!=k)
>> printf("%d %d\n", j, k);
>> }
>> }
>>
>> 1: if these lines are commented out, then the printf is never called,
>> but if uncommented (along with using different constant values), then I
>> start seeing messages (with it off-by-one, rounded towards 0).

That suggests that the round-trip conversion is happening as expected.

>> 2: if I switch to the second form, which makes the fudging, then the
>> messages disappear (they still appear with the first form).

And this is no longer a mystery.  If the +1.0 and -1.0 is producing a
non-integer result, then, yes, this fudge factor will repair it.

>> so, it would seem to be mostly an issue in this case of whether or not
>> one does any arithmetic on the values (not sure whether or not this
>> still counts). CPU is an "AMD Athlon II X4 630".

I see nothing on my Intel hardware (gcc version 4.6.1).

>> or, at least, this is what I am seeing here...
>>
>
> oh yeah, here is an example of the output (with a slight tweak to show
> the value held by the double):
> 1073741824 1073741823 41CFFFFFFFFFFFFF
> 1073741824 1073741823 41CFFFFFFFFFFFFF
> 262144 262143 410FFFFFFFFFFFFF
> 262144 262143 410FFFFFFFFFFFFF
> 262144 262143 410FFFFFFFFFFFFF
> 67108864 67108863 418FFFFFFFFFFFFF
> 4 3 400FFFFFFFFFFFFF
> 1073741824 1073741823 41CFFFFFFFFFFFFF
> 1073741824 1073741823 41CFFFFFFFFFFFFF
> 1073741824 1073741823 41CFFFFFFFFFFFFF
> 4194304 4194303 414FFFFFFFFFFFFF
> 4194304 4194303 414FFFFFFFFFFFFF
> 16384 16383 40CFFFFFFFFFFFFF
> 67108864 67108863 418FFFFFFFFFFFFF
> 16384 16383 40CFFFFFFFFFFFFF
>
> hmm, a lot of the same values seem to repeat...

They are more interesting in hex. (0x4, 0x4000, 0x40000 and so on).

If the result is reliable with 4 (i.e. if you take the loop out, set
j = 4 and find that j != k) it will be simple to see if it the +1.0 or
-1.0 that leads to the loss of precision.

However, this is not a problem for the C implementation because the
accuracy of floating point arithmetic is implementation defined.  It may
be a problem for the chip, in that the specification might be that this
should not happen, but such error are very rare, so it's more likely to
be by design.

You said elsewhere that there might have been arithmetic being done.
Had that come right away, we could have cut the whole discussion!

-- 
Ben.

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


#14025

FromRobert Wessel <robertwessel2@yahoo.com>
Date2012-02-14 00:24 -0600
Message-ID<lesjj7tal8cu07kkq93068f4vl2a5ntr13@4ax.com>
In reply to#14008
On Mon, 13 Feb 2012 18:31:48 -0700, BGB <cr88192@hotmail.com> wrote:

>On 2/13/2012 5:46 PM, Ben Bacarisse wrote:
>> BGB<cr88192@hotmail.com>  writes:
>>
>>> On 2/13/2012 3:00 PM, Ben Bacarisse wrote:
>>>> BGB<cr88192@hotmail.com>   writes:
>>>> <snip>
>>>>> also, even though double has more bits than, say, an integer, does not
>>>>> mean it will reliably encode an integer's value (it can do so in
>>>>> theory, and will most often do so, but whether or not it will actually
>>>>> always do so is more "up for grabs").
>>>>
>>>> It's not up for grabs in C (and C++ is essentially the same in this
>>>> regard).  If the integer can be represented exactly in the floating
>>>> point type, it must be.
>>>>
>>>
>>> theoretically, yes.
>>>
>>> given a double has 52 bits, and int is 32 bits, and it is possible to
>>> convert exactly, it should always be reliable.
>>>
>>> but, I have seen it not work exactly right, albeit in rare cases (IME,
>>> usually on 64-bit Linux systems, generally fairly rare and when using
>>> AMD chips IME).
>>>
>>> I haven't seen the issue on Win64 though that I can remember, nor with
>>> 32-bit code, so I don't know.
>>>
>>> it "might" have something to do with SSE for all I know (since 32-bit
>>> code typically uses x87, and 64-bit typically uses SSE), or maybe
>>> something to do with GCC, or similar.
>>>
>>> might require researching, like trying to figure how exactly AMD chips
>>> implement the "cvtsi2sd" and "cvtsd2si" instructions or similar... (I
>>> am not even particularly sure which side of the conversion would have
>>> been introducing a loss of accuracy, or if the cause could be
>>> something else "in the middle" somewhere).
>>
>> Since this behaviour is required for C implementations to be conforming,
>> deviation from it is important.  Was there perhaps a bug report filed?
>>
>
>not at the time, I merely thought of it as an interesting occurrence and 
>worked around it.
>
>
>> <snip>
>>>>> one can counteract this by fudging the value with a small epsilon
>>>>> prior to converting back into an integer.
>>>>>
>>>>> say, for example (untested, from memory):
>>>>> (v>=0)?((int)(v+0.0001)):((int)(v-0.0001));
>>>>
>>>> I can't see how this helps.  If v is representable exactly as a double,
>>>> the round trip has no effect so this code is not needed. Can you give me
>>>> a use-case?
>>>>
>>>
>>> generally, I had seen it in my 3D engine, where in some cases integers
>>> ended up getting converted to doubles and back, and sometimes they
>>> would get "bumped" in this way. adding a small adjustment seemed to
>>> fix the problem.
>>
>> Can you add a test to the code to print v when
>>
>>   (int)(double)v != v&&
>>   (v>= 0 ? (int)(v+0.0001) : (int)(v-0.0001)) == v
>>
>> ?  That way we might get an example of the problem you are reporting.
>>
>
>I would have to find an example of it again...
>
>I remember seeing the problem a few years ago in some code of mine, but 
>don't have any recent memory of bugs resulting from it (but, then again, 
>this could also be due to code paranoia...).
>
>just went and tried to recreate it, with mixed results:
>a raw conversion does not show any issues (seems to always be reliable);
>if I add a value to the double, and subtract the same value, then it 
>starts acting up.
>
>testing the code below in Fedora 13 x86-64 within VMware (yes, not the 
>raw HW, but I would otherwise have to reboot).
>
>#include <stdio.h>
>
>int main()
>{
>	double d;
>	int i, j, k;
>
>	for(i=0; i<100000000; i++)
>	{
>		j=rand()*rand()*i;
>		d=j;
>		d=d+1.0; //(1)
>		d=d-1.0; //(1)
>		k=d; //(2)
>		k=(d>=0?(int)(d+0.0001):(int)(d-0.0001)); //(2)
>		if(j!=k)
>			printf("%d %d\n", j, k);
>	}
>}
>
>1: if these lines are commented out, then the printf is never called, 
>but if uncommented (along with using different constant values), then I 
>start seeing messages (with it off-by-one, rounded towards 0).
>
>2: if I switch to the second form, which makes the fudging, then the 
>messages disappear (they still appear with the first form).
>
>so, it would seem to be mostly an issue in this case of whether or not 
>one does any arithmetic on the values (not sure whether or not this 
>still counts). CPU is an "AMD Athlon II X4 630".
>
>or, at least, this is what I am seeing here...
>
>
>here is the inner part of the loop (in ASM):
>.L4:
>	movl	$0, %eax
>	call	rand
>	movl	%eax, %ebx
>	movl	$0, %eax
>	call	rand
>	imull	%ebx, %eax
>	imull	-20(%rbp), %eax
>	movl	%eax, -24(%rbp)
>	cvtsi2sd	-24(%rbp), %xmm0
>	movsd	%xmm0, -32(%rbp)

>	movsd	-32(%rbp), %xmm1
>	movsd	.LC0(%rip), %xmm0
>	addsd	%xmm1, %xmm0
>	movsd	%xmm0, -32(%rbp)
>	movsd	-32(%rbp), %xmm0
>	movsd	.LC0(%rip), %xmm1
>	subsd	%xmm1, %xmm0
>	movsd	%xmm0, -32(%rbp)
>	movsd	-32(%rbp), %xmm0
>	cvttsd2si	%xmm0, %eax
>	movl	%eax, -36(%rbp)
>	movl	-24(%rbp), %eax
>	cmpl	-36(%rbp), %eax
>	je	.L3
>	movl	$.LC1, %eax
>	movl	-36(%rbp), %edx
>	movl	-24(%rbp), %ecx
>	movl	%ecx, %esi
>	movq	%rax, %rdi
>	movl	$0, %eax
>	call	printf
>.L3:
>
>...
>
>.LC0:
>	.long	0
>	.long	1072693248


The explanation for this, at least, seems simple.  You're overflowing
the dynamic range of a double.  32767*32767*100000000 needs a bit more
than 56 bits to represent exactly.  That doesn't fit in a double
(assuming an IEEE double, of course).  Nor do the results of that
number plus or minus 1.0.  If the ints on your system are 32 bit,
there should be no problem, as the number will be reduced to 32 bits
first, if they're 64 bits, you'll have round off error.

I've not looks through all the combinations, but the compilers could
be setting rounding differently, and even for x87 and SSE2
instructions rounding can be set differently at the same time.
Rounding can be different between the x87 store-as-int (FIST rounds
according to the selected x87 rounding mode), SSE2 convert-to-int
(CVTTSD2S always truncates towards zero) and software convert-to-int
(which can do whatever the implementers fancied).  Those different
rounding modes could to a lesser or greater degree be masking the
problem.  Another variable is that on some implementations, the x87 FP
calculations are done in 80-bit format, at least some of the time, so
you might see no round off errors at all in those cases.

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


#14027

FromBGB <cr88192@hotmail.com>
Date2012-02-13 23:42 -0700
Message-ID<jhcvqs$64m$1@news.albasani.net>
In reply to#14025
On 2/13/2012 11:24 PM, Robert Wessel wrote:
> On Mon, 13 Feb 2012 18:31:48 -0700, BGB<cr88192@hotmail.com>  wrote:
>
>> On 2/13/2012 5:46 PM, Ben Bacarisse wrote:
>>> BGB<cr88192@hotmail.com>   writes:
>>>
>>>> On 2/13/2012 3:00 PM, Ben Bacarisse wrote:
>>>>> BGB<cr88192@hotmail.com>    writes:
>>>>> <snip>
>>>>>> also, even though double has more bits than, say, an integer, does not
>>>>>> mean it will reliably encode an integer's value (it can do so in
>>>>>> theory, and will most often do so, but whether or not it will actually
>>>>>> always do so is more "up for grabs").
>>>>>
>>>>> It's not up for grabs in C (and C++ is essentially the same in this
>>>>> regard).  If the integer can be represented exactly in the floating
>>>>> point type, it must be.
>>>>>
>>>>
>>>> theoretically, yes.
>>>>
>>>> given a double has 52 bits, and int is 32 bits, and it is possible to
>>>> convert exactly, it should always be reliable.
>>>>
>>>> but, I have seen it not work exactly right, albeit in rare cases (IME,
>>>> usually on 64-bit Linux systems, generally fairly rare and when using
>>>> AMD chips IME).
>>>>
>>>> I haven't seen the issue on Win64 though that I can remember, nor with
>>>> 32-bit code, so I don't know.
>>>>
>>>> it "might" have something to do with SSE for all I know (since 32-bit
>>>> code typically uses x87, and 64-bit typically uses SSE), or maybe
>>>> something to do with GCC, or similar.
>>>>
>>>> might require researching, like trying to figure how exactly AMD chips
>>>> implement the "cvtsi2sd" and "cvtsd2si" instructions or similar... (I
>>>> am not even particularly sure which side of the conversion would have
>>>> been introducing a loss of accuracy, or if the cause could be
>>>> something else "in the middle" somewhere).
>>>
>>> Since this behaviour is required for C implementations to be conforming,
>>> deviation from it is important.  Was there perhaps a bug report filed?
>>>
>>
>> not at the time, I merely thought of it as an interesting occurrence and
>> worked around it.
>>
>>
>>> <snip>
>>>>>> one can counteract this by fudging the value with a small epsilon
>>>>>> prior to converting back into an integer.
>>>>>>
>>>>>> say, for example (untested, from memory):
>>>>>> (v>=0)?((int)(v+0.0001)):((int)(v-0.0001));
>>>>>
>>>>> I can't see how this helps.  If v is representable exactly as a double,
>>>>> the round trip has no effect so this code is not needed. Can you give me
>>>>> a use-case?
>>>>>
>>>>
>>>> generally, I had seen it in my 3D engine, where in some cases integers
>>>> ended up getting converted to doubles and back, and sometimes they
>>>> would get "bumped" in this way. adding a small adjustment seemed to
>>>> fix the problem.
>>>
>>> Can you add a test to the code to print v when
>>>
>>>    (int)(double)v != v&&
>>>    (v>= 0 ? (int)(v+0.0001) : (int)(v-0.0001)) == v
>>>
>>> ?  That way we might get an example of the problem you are reporting.
>>>
>>
>> I would have to find an example of it again...
>>
>> I remember seeing the problem a few years ago in some code of mine, but
>> don't have any recent memory of bugs resulting from it (but, then again,
>> this could also be due to code paranoia...).
>>
>> just went and tried to recreate it, with mixed results:
>> a raw conversion does not show any issues (seems to always be reliable);
>> if I add a value to the double, and subtract the same value, then it
>> starts acting up.
>>
>> testing the code below in Fedora 13 x86-64 within VMware (yes, not the
>> raw HW, but I would otherwise have to reboot).
>>
>> #include<stdio.h>
>>
>> int main()
>> {
>> 	double d;
>> 	int i, j, k;
>>
>> 	for(i=0; i<100000000; i++)
>> 	{
>> 		j=rand()*rand()*i;
>> 		d=j;
>> 		d=d+1.0; //(1)
>> 		d=d-1.0; //(1)
>> 		k=d; //(2)
>> 		k=(d>=0?(int)(d+0.0001):(int)(d-0.0001)); //(2)
>> 		if(j!=k)
>> 			printf("%d %d\n", j, k);
>> 	}
>> }
>>
>> 1: if these lines are commented out, then the printf is never called,
>> but if uncommented (along with using different constant values), then I
>> start seeing messages (with it off-by-one, rounded towards 0).
>>
>> 2: if I switch to the second form, which makes the fudging, then the
>> messages disappear (they still appear with the first form).
>>
>> so, it would seem to be mostly an issue in this case of whether or not
>> one does any arithmetic on the values (not sure whether or not this
>> still counts). CPU is an "AMD Athlon II X4 630".
>>
>> or, at least, this is what I am seeing here...
>>
>>
>> here is the inner part of the loop (in ASM):
>> .L4:
>> 	movl	$0, %eax
>> 	call	rand
>> 	movl	%eax, %ebx
>> 	movl	$0, %eax
>> 	call	rand
>> 	imull	%ebx, %eax
>> 	imull	-20(%rbp), %eax
>> 	movl	%eax, -24(%rbp)
>> 	cvtsi2sd	-24(%rbp), %xmm0
>> 	movsd	%xmm0, -32(%rbp)
>
>> 	movsd	-32(%rbp), %xmm1
>> 	movsd	.LC0(%rip), %xmm0
>> 	addsd	%xmm1, %xmm0
>> 	movsd	%xmm0, -32(%rbp)
>> 	movsd	-32(%rbp), %xmm0
>> 	movsd	.LC0(%rip), %xmm1
>> 	subsd	%xmm1, %xmm0
>> 	movsd	%xmm0, -32(%rbp)
>> 	movsd	-32(%rbp), %xmm0
>> 	cvttsd2si	%xmm0, %eax
>> 	movl	%eax, -36(%rbp)
>> 	movl	-24(%rbp), %eax
>> 	cmpl	-36(%rbp), %eax
>> 	je	.L3
>> 	movl	$.LC1, %eax
>> 	movl	-36(%rbp), %edx
>> 	movl	-24(%rbp), %ecx
>> 	movl	%ecx, %esi
>> 	movq	%rax, %rdi
>> 	movl	$0, %eax
>> 	call	printf
>> .L3:
>>
>> ...
>>
>> .LC0:
>> 	.long	0
>> 	.long	1072693248
>
>
> The explanation for this, at least, seems simple.  You're overflowing
> the dynamic range of a double.  32767*32767*100000000 needs a bit more
> than 56 bits to represent exactly.  That doesn't fit in a double
> (assuming an IEEE double, of course).  Nor do the results of that
> number plus or minus 1.0.  If the ints on your system are 32 bit,
> there should be no problem, as the number will be reduced to 32 bits
> first, if they're 64 bits, you'll have round off error.
>

this is on x86 and x86-64, and in both cases "int" is 32 bits.
storing the expression into an int essentially truncates it to 32-bits.

if it were a "long long", it would be a different matter.


> I've not looks through all the combinations, but the compilers could
> be setting rounding differently, and even for x87 and SSE2
> instructions rounding can be set differently at the same time.
> Rounding can be different between the x87 store-as-int (FIST rounds
> according to the selected x87 rounding mode), SSE2 convert-to-int
> (CVTTSD2S always truncates towards zero) and software convert-to-int
> (which can do whatever the implementers fancied).  Those different
> rounding modes could to a lesser or greater degree be masking the
> problem.  Another variable is that on some implementations, the x87 FP
> calculations are done in 80-bit format, at least some of the time, so
> you might see no round off errors at all in those cases.

yeah.

dunno the what exactly is the issue, but it is probably fairly minor 
given the lack of any obvious problems...

as noted, when I had seen it before, I had simply assumed that it was 
some basic property of floating-point behavior and fudged it. if other 
people have been seeing similar behavior, it is possible maybe such 
fudging is fairly common?...

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


#14035

FromJames Kuyper <jameskuyper@verizon.net>
Date2012-02-14 06:48 -0500
Message-ID<jhdhll$2dh$1@dont-email.me>
In reply to#14025
On 02/14/2012 01:24 AM, Robert Wessel wrote:
> On Mon, 13 Feb 2012 18:31:48 -0700, BGB <cr88192@hotmail.com> wrote:
...
>> #include <stdio.h>
>>
>> int main()
>> {
>> 	double d;
>> 	int i, j, k;
>>
>> 	for(i=0; i<100000000; i++)
>> 	{
>> 		j=rand()*rand()*i;
>> 		d=j;
>> 		d=d+1.0; //(1)
>> 		d=d-1.0; //(1)
>> 		k=d; //(2)
>> 		k=(d>=0?(int)(d+0.0001):(int)(d-0.0001)); //(2)
>> 		if(j!=k)
>> 			printf("%d %d\n", j, k);
>> 	}
>> }
>>
>> 1: if these lines are commented out, then the printf is never called, 
>> but if uncommented (along with using different constant values), then I 
>> start seeing messages (with it off-by-one, rounded towards 0).

That's to be expected. As i gets larger, for some of the values that can
be returned by rand(),
>>
>> 2: if I switch to the second form, which makes the fudging, then the 
>> messages disappear (they still appear with the first form).
>>
>> so, it would seem to be mostly an issue in this case of whether or not 
>> one does any arithmetic on the values (not sure whether or not this 
>> still counts). CPU is an "AMD Athlon II X4 630".
>>
>> or, at least, this is what I am seeing here...
...
> The explanation for this, at least, seems simple.  You're overflowing
> the dynamic range of a double.  32767*32767*100000000 needs a bit more
> than 56 bits to represent exactly.

That number is 32767*32767*5^8 * 2^8. Representing the first part
exactly only requires 48 bits, and the 2^8 just shifts the exponent.
However, the actual maximum value that d can have is
32767*32767*99999999, and that does require 56 bits to represent exactly.

> ...  That doesn't fit in a double
> (assuming an IEEE double, of course).  Nor do the results of that
> number plus or minus 1.0. ...

That part is true, even when 'i' has a large power of 2 among it's factors.

There's no mystery about these results anymore - he's looking at integer
values that are too large for N and N+1 to both be exactly representable.
-- 
James Kuyper

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


#14037

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2012-02-14 13:51 +0000
Message-ID<0.ec8f8a4139fae2f0717c.20120214135119GMT.874nut34mg.fsf@bsb.me.uk>
In reply to#14035
James Kuyper <jameskuyper@verizon.net> writes:

> On 02/14/2012 01:24 AM, Robert Wessel wrote:
>> On Mon, 13 Feb 2012 18:31:48 -0700, BGB <cr88192@hotmail.com> wrote:
> ...
>>> #include <stdio.h>
>>>
>>> int main()
>>> {
>>> 	double d;
>>> 	int i, j, k;
>>>
>>> 	for(i=0; i<100000000; i++)
>>> 	{
>>> 		j=rand()*rand()*i;
>>> 		d=j;
>>> 		d=d+1.0; //(1)
>>> 		d=d-1.0; //(1)
>>> 		k=d; //(2)
>>> 		k=(d>=0?(int)(d+0.0001):(int)(d-0.0001)); //(2)
[The reported error come when the above line is commented out]
>>> 		if(j!=k)
>>> 			printf("%d %d\n", j, k);
>>> 	}
>>> }
<snip>
>> ...  That doesn't fit in a double
>> (assuming an IEEE double, of course).  Nor do the results of that
>> number plus or minus 1.0. ...
>
> That part is true, even when 'i' has a large power of 2 among it's factors.
>
> There's no mystery about these results anymore - he's looking at integer
> values that are too large for N and N+1 to both be exactly
> representable.

That's not what's happening.  On the system in question, int has only
32 bits.  The large and possibly overflowing value is assigned to an
int first.  This conversion is implementation defined, but it can't make
j too large to represented exactly in d.  In fact, one of his reported
error cases was caused when j == 4.  This *must* set d to 4.0 (and it
did).  The subsequent +1.0 and -1.0 and conversion back to int produces
a value not == 4.

I agree there is no mystery, but it's not using integers too large for
double -- it's floating-point arithmetic is not exact.

[Aside: since the integer arithmetic can overflow, technically the
program has undefined behaviour.  So while d may be assigned 4.0, say,
it is quite permissible (from C's point of view) for it to become 5.0 or
109.2 or any other value at any time.  I'm assuming a sane
implementation.]

-- 
Ben.

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


#14040

FromBGB <cr88192@hotmail.com>
Date2012-02-14 08:42 -0700
Message-ID<jhdvf0$b93$1@news.albasani.net>
In reply to#14035
On 2/14/2012 4:48 AM, James Kuyper wrote:
> On 02/14/2012 01:24 AM, Robert Wessel wrote:
>> On Mon, 13 Feb 2012 18:31:48 -0700, BGB<cr88192@hotmail.com>  wrote:
> ...
>>> #include<stdio.h>
>>>
>>> int main()
>>> {
>>> 	double d;
>>> 	int i, j, k;
>>>
>>> 	for(i=0; i<100000000; i++)
>>> 	{
>>> 		j=rand()*rand()*i;
>>> 		d=j;
>>> 		d=d+1.0; //(1)
>>> 		d=d-1.0; //(1)
>>> 		k=d; //(2)
>>> 		k=(d>=0?(int)(d+0.0001):(int)(d-0.0001)); //(2)
>>> 		if(j!=k)
>>> 			printf("%d %d\n", j, k);
>>> 	}
>>> }
>>>
>>> 1: if these lines are commented out, then the printf is never called,
>>> but if uncommented (along with using different constant values), then I
>>> start seeing messages (with it off-by-one, rounded towards 0).
>
> That's to be expected. As i gets larger, for some of the values that can
> be returned by rand(),
>>>
>>> 2: if I switch to the second form, which makes the fudging, then the
>>> messages disappear (they still appear with the first form).
>>>
>>> so, it would seem to be mostly an issue in this case of whether or not
>>> one does any arithmetic on the values (not sure whether or not this
>>> still counts). CPU is an "AMD Athlon II X4 630".
>>>
>>> or, at least, this is what I am seeing here...
> ...
>> The explanation for this, at least, seems simple.  You're overflowing
>> the dynamic range of a double.  32767*32767*100000000 needs a bit more
>> than 56 bits to represent exactly.
>
> That number is 32767*32767*5^8 * 2^8. Representing the first part
> exactly only requires 48 bits, and the 2^8 just shifts the exponent.
> However, the actual maximum value that d can have is
> 32767*32767*99999999, and that does require 56 bits to represent exactly.
>

but, if you store it into a 32 bit integer (first), all the high-order 
bits end up cut off anyways. a desktop PC doesn't have any such 64-bit 
"int" type, so the above can't be the issue.


>> ...  That doesn't fit in a double
>> (assuming an IEEE double, of course).  Nor do the results of that
>> number plus or minus 1.0. ...
>
> That part is true, even when 'i' has a large power of 2 among it's factors.
>
> There's no mystery about these results anymore - he's looking at integer
> values that are too large for N and N+1 to both be exactly representable.

no, that is not the problem here.

I suspect results would be far more obvious if something like this was 
going on (not just a few misses, but an absurdly long list of misses, 
all with huge values...).

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


#14051

Fromglen herrmannsfeldt <gah@ugcs.caltech.edu>
Date2012-02-14 19:58 +0000
Message-ID<jheed9$1g4$1@speranza.aioe.org>
In reply to#14040
In comp.lang.c++ BGB <cr88192@hotmail.com> wrote:

(snip)
> but, if you store it into a 32 bit integer (first), all the high-order 
> bits end up cut off anyways. a desktop PC doesn't have any such 64-bit 
> "int" type, so the above can't be the issue.

IA32 doesn't do it in one instruction, but many compilers targeting
the 32 bit IA32 have a 64 bit (long long) data type. Add and 
subtract can usually be done inline, with two instructions.
Multiply and divide might be done as subroutine call, but many
will recognize the case of 32 bit operands cast to (long long),
and use a single multiply instruction with 64 bit product.

I never tested for it, but they might also recognize the case of
a 64 bit dividend and 32 bit divisor, generating a 32 bit quotient.

(But a large fraction of desktop PCs now have 64 bit processors.)

-- glen

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


Page 3 of 4 — ← Prev page 1 2 [3] 4  Next page →

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


csiph-web