Why do we need `volatile` in C++?

519 views
Skip to first unread message

Mingxin Wang

unread,
Mar 23, 2018, 2:20:57 AM3/23/18
to ISO C++ Standard - Future Proposals
I am confusing about the necessity of the keyword `volatile` in C++, as we already have the atomics, and this keyword is not able to deal with processor optimizations. Is there any meaningful use case that depends on this keyword in C++ (except for the atomic library in C)? If not, I think it shall be deprecated or removed, like the keyword `register`.

I am looking forward to your comments!

Mingxin Wang

Nicolas Lesser

unread,
Mar 23, 2018, 2:31:02 AM3/23/18
to std-pr...@isocpp.org
`volatile` aren't supposed to be used as thread safe storage, they aren't designed to do that and any code that relies on that specific feature is broken.

You should use volatile when loading or storing a variable causes side effects outside of your program. For example:

- You set a pointer to volatile value to 0, so that the flow of blood from some device stops.
- You read a value from a pointer to volatile to get the current temperature from a smart thermometer.

Basically, whenever loading or storing to a value has side effects that the compiler doesn't know about. The compiler is this prohibited to optimize such accesses to volatile. This is a feature, not a bug.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/044dfdd3-2313-4ce3-a5e2-3a23c1c0d678%40isocpp.org.

Mingxin Wang

unread,
Mar 23, 2018, 3:12:11 AM3/23/18
to ISO C++ Standard - Future Proposals
On Friday, March 23, 2018 at 2:31:02 PM UTC+8, Nicolas Lesser wrote:
`volatile` aren't supposed to be used as thread safe storage, they aren't designed to do that and any code that relies on that specific feature is broken.

You should use volatile when loading or storing a variable causes side effects outside of your program. For example:

- You set a pointer to volatile value to 0, so that the flow of blood from some device stops.
- You read a value from a pointer to volatile to get the current temperature from a smart thermometer.

Basically, whenever loading or storing to a value has side effects that the compiler doesn't know about. The compiler is this prohibited to optimize such accesses to volatile. This is a feature, not a bug.

The problem is that `volatile` only works at compile-time, rather than at runtime, and the processor optimization may also perform reordering. If my understanding is right, the only way to prohibite runtime reordering, guaranteed by the standard, is to use atomics and fences.

Nicolas Lesser

unread,
Mar 23, 2018, 3:21:20 AM3/23/18
to std-pr...@isocpp.org
I would be highly surprised if a processor reorders instructions as a form of optimization. I know it can execute multiple loop bodies at the same time and other such optimization, such as speculative execution, but they don't change the effective order of instructions.The instructions are still executed as-if they are executed in order.

I'm pretty sure that the processor will not change the effective order of instructions, as that would make any program behave in odd ways, regardless of whether volatile is used or not. Do you have an example of such a optimization?

Also, if the processor can reorder instructions, then it could also reorder the instructions that were supposed to be protected by a fence/barrier/...

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Ville Voutilainen

unread,
Mar 23, 2018, 3:32:06 AM3/23/18
to ISO C++ Standard - Future Proposals
On 23 March 2018 at 09:21, Nicolas Lesser <blitz...@gmail.com> wrote:
> I would be highly surprised if a processor reorders instructions as a form
> of optimization. I know it can execute multiple loop bodies at the same time
> and other such optimization, such as speculative execution, but they don't
> change the effective order of instructions.The instructions are still
> executed as-if they are executed in order.
>
> I'm pretty sure that the processor will not change the effective order of
> instructions, as that would make any program behave in odd ways, regardless
> of whether volatile is used or not. Do you have an example of such a
> optimization?
>
> Also, if the processor can reorder instructions, then it could also reorder
> the instructions that were supposed to be protected by a fence/barrier/...


*bats eyelids* huh what? This
https://en.wikipedia.org/wiki/Out-of-order_execution has been a thing
for decades. The processor doesn't reorder around fences and barriers
because the compiler lays
down instructions that prevent the processor from doing so.

Magnus Fromreide

unread,
Mar 23, 2018, 5:01:25 AM3/23/18
to std-pr...@isocpp.org
volatile tells the compiler that the storage location might be changed by
things outside it's control.

Example:

volatile struct memory_mapped_thingie* thingieP;

for (;;) {
if (thingieP->flag) {
thingieP->control = value;
}
}

Without volatile the compiler is allowed to transform that code into

if (thingieP->flag) {
for (;;) {
thingieP->control = value;
}
} else {
for (;;)
;
}

since it knows that it never writes to thingieP->flag.

/MF

Mingxin Wang

unread,
Mar 23, 2018, 6:42:31 AM3/23/18
to ISO C++ Standard - Future Proposals
On Friday, March 23, 2018 at 5:01:25 PM UTC+8, Magnus Fromreide wrote:
volatile tells the compiler that the storage location might be changed by
things outside it's control.

Example:

volatile struct memory_mapped_thingie* thingieP;

for (;;) {
  if (thingieP->flag) {
    thingieP->control = value;
  }
}

Without volatile the compiler is allowed to transform that code into

if (thingieP->flag) {
  for (;;) {
    thingieP->control = value;
  }
} else {
  for (;;)
     ;
}

since it knows that it never writes to thingieP->flag.

/MF

The keyword indeed could prevent such optimization. However, in the case you provided, there seems to be no guarantee for the visibility of `thingieP->flag` and `thingieP->control`, which introduces bugs that hard to find. For example, the assignment `thingieP->control = value` may be ignored after processor optimization since the value is never acquired in the current thread. Thus, I do not think this case is a meaningful one to the keyword `volatile`.

Richard Hodges

unread,
Mar 23, 2018, 6:52:57 AM3/23/18
to std-pr...@isocpp.org
This is precisely the meaning of 'volatile'. It forces the compiler to emit a write to memory regardless of whether the value will be read back. It is designed for reading and writing values to memory mapped I/O, where the very act of placing signals on the bus has real consequences for the hardware.

Remember that not all memory-mapped I/O behaves like RAM. Sometimes, reading from a mapped address can cause a signal to be sent to a peripheral (i.e. read is really a write). volatile is the way c and c++ express this relationship with hardware.



 

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Ville Voutilainen

unread,
Mar 23, 2018, 7:01:48 AM3/23/18
to ISO C++ Standard - Future Proposals
On 23 March 2018 at 12:52, Richard Hodges <hodg...@gmail.com> wrote:
>> The keyword indeed could prevent such optimization. However, in the case
>> you provided, there seems to be no guarantee for the visibility of
>> `thingieP->flag` and `thingieP->control`, which introduces bugs that hard to
>> find. For example, the assignment `thingieP->control = value` may be ignored
>> after processor optimization since the value is never acquired in the
>> current thread. Thus, I do not think this case is a meaningful one to the
>> keyword `volatile`.
>
>
> This is precisely the meaning of 'volatile'. It forces the compiler to emit
> a write to memory regardless of whether the value will be read back. It is
> designed for reading and writing values to memory mapped I/O, where the very
> act of placing signals on the bus has real consequences for the hardware.
>
> Remember that not all memory-mapped I/O behaves like RAM. Sometimes, reading
> from a mapped address can cause a signal to be sent to a peripheral (i.e.
> read is really a write). volatile is the way c and c++ express this
> relationship with hardware.


While the semantics of volatile access are implementation-defined,
volatile access is a side-effect,
so there's little to no danger of a compiler optimizing it away.

Miguel Ojeda

unread,
Mar 23, 2018, 7:05:41 AM3/23/18
to std-pr...@isocpp.org
Indeed, modern processors effectively rewrite the program, not just
"reorder" the instructions. However, Nicolas is correct in the sense
that however that is performed, the external behavior must be
preserved w.r.t. the guarantees that the architecture gives for each
instruction. The same way optimizing C++ compilers can perform all
kinds of transformations as long as the program behaves within the
promises the specification gives.

Of course, volatile has always been in both C and C++ a hairy subject
because implementations may choose to give it stronger/different
semantics than required by the standard because users de facto gave
them those semantics and now they don't want to break their clients'
code.

Cheers,
Miguel

Thiago Macieira

unread,
Mar 23, 2018, 7:40:24 AM3/23/18
to std-pr...@isocpp.org
On Friday, 23 March 2018 15:12:11 CST Mingxin Wang wrote:
> The problem is that `volatile` only works at compile-time, rather than at
> runtime, and the processor optimization may also perform reordering. If my
> understanding is right, the only way to prohibite runtime reordering,
> guaranteed by the standard, is to use atomics and fences.

Sure it works at runtime. You use volatile in memory regions where the
processor is configured not to coalesce, use caches or delay writebacks.
Basically, you use volatile in UC MMIO memory.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center



Bengt Gustafsson

unread,
Mar 25, 2018, 8:52:32 AM3/25/18
to ISO C++ Standard - Future Proposals
While all of this is true volatile sees very little use compared to its rather large effects on the standard. I'm pretty sure that if we had a fresh start
handling of memory mapped IO would be relegated to a few library intrinsics. Maybe it could even be deprecated eventually if such intrinsics were introduced in the next standard. Then again, now that we have taken volatile this far it may not be worth the while to get rid of.

Hyman Rosen

unread,
Mar 25, 2018, 9:27:08 AM3/25/18
to std-pr...@isocpp.org
On Sun, Mar 25, 2018, 8:52 AM Bengt Gustafsson <bengt.gu...@beamways.com> wrote:
While all of this is true volatile sees very little use compared to its rather large effects on the standard. I'm pretty sure that if we had a fresh start
handling of memory mapped IO would be relegated to a few library intrinsics.

I've had to use volatile distressingly often, and it has nothing to do with memory-mapped IO.  I use it to force assignments to floating-point variables to be written to memory, denying the compiler permission to use the value from an extended-precision register instead.  Volatile can go only when this permission is revoked (or gcc is changed to obey the rules properly, which won't happen because of optimizationism).

Thiago Macieira

unread,
Mar 25, 2018, 9:48:03 AM3/25/18
to std-pr...@isocpp.org
While I don't subscribe to Hyman's philosophy regarding optimisation, he does
have a point on ensuring memory reads and writes. std::atomic is specified
with methods both volatile and non-volatile and that makes a difference.
Atomicity and volatility are not the same concept, especially for base
operations like load and store.

Mingxin Wang

unread,
Mar 25, 2018, 10:34:17 PM3/25/18
to ISO C++ Standard - Future Proposals
On Sunday, March 25, 2018 at 9:48:03 PM UTC+8, Thiago Macieira wrote:
While I don't subscribe to Hyman's philosophy regarding optimisation, he does
have a point on ensuring memory reads and writes. std::atomic is specified
with methods both volatile and non-volatile and that makes a difference.
Atomicity and volatility are not the same concept, especially for base
operations like load and store.

As far as I am concerned, there seems to be no guarantee in the standard that `volatile` could prevent using cache, although some compilers promise that.

By the way, is there any meaningful use case where the atomics shall be declared `volatile`?

Thank you for the comment!

Victor Dyachenko

unread,
Mar 26, 2018, 3:15:02 AM3/26/18
to ISO C++ Standard - Future Proposals
volatile T *p = ..;
assert(*p == *p);

This assert can fire for volatile pointer but never for non-volatile.

It's because the compiler must load the value twice (that can change between loads). For no-volatile case only one load can be done.

Sashan Govender

unread,
Mar 26, 2018, 3:50:46 AM3/26/18
to std-pr...@isocpp.org
On Mon, 26 Mar 2018 at 13:34 Mingxin Wang <wmx16...@163.com> wrote:
On Sunday, March 25, 2018 at 9:48:03 PM UTC+8, Thiago Macieira wrote:
While I don't subscribe to Hyman's philosophy regarding optimisation, he does
have a point on ensuring memory reads and writes. std::atomic is specified
with methods both volatile and non-volatile and that makes a difference.
Atomicity and volatility are not the same concept, especially for base
operations like load and store.

As far as I am concerned, there seems to be no guarantee in the standard that `volatile` could prevent using cache, although some compilers promise that.


How a compiler handles volatile and lack of optimization is implementation defined. If an implementation did read the cache instead of the memory location and the side-effect from reading that memory location was not preserved, then it's a bug in the implementation.
 

David Brown

unread,
Mar 26, 2018, 8:03:15 AM3/26/18
to std-pr...@isocpp.org
When your code uses volatile reads and writes, the compiler will issue
exactly as many reads and writes as given, in the order given. That is
the basic principle of "volatile" in C and C++. Compilers can give
/more/ than that - some treat any volatile access as a memory fence, for
example. And there are differences in complicated things like volatile
bitfields, volatile read-modify-write expressions (like "vol++;"), etc.
It is usually best to avoid these. Also, the standards don't make it
clear that using a "pointer to volatile" is a volatile access - though
every compiler known to man treats it that way.

The cpu may re-order instructions and memory accesses in all sorts of
ways - other than for a few architectures, it does not know that an
access is "volatile".

But if you are using "volatile" correctly, you are using it for things
like memory mapped peripherals (where MMU settings generally enforce
access orderings) or in combination with synchronisation and fence
instructions (from before C++11 or C11). Using "volatile" alone is not
safe for threading or SMP.


Thiago Macieira

unread,
Mar 26, 2018, 8:56:24 PM3/26/18
to std-pr...@isocpp.org
On segunda-feira, 26 de março de 2018 10:34:17 CST Mingxin Wang wrote:
> On Sunday, March 25, 2018 at 9:48:03 PM UTC+8, Thiago Macieira wrote:
> > While I don't subscribe to Hyman's philosophy regarding optimisation, he
> > does
> > have a point on ensuring memory reads and writes. std::atomic is specified
> > with methods both volatile and non-volatile and that makes a difference.
> > Atomicity and volatility are not the same concept, especially for base
> > operations like load and store.
>
> As far as I am concerned, there seems to be no guarantee in the standard
> that `volatile` could prevent using cache, although some compilers promise
> that.

There isn't. Volatile is a tool you use when your specific memory type
requires it. If you need to make that particular memory region non-cached, you
need to read your architecture system manual.

> By the way, is there any meaningful use case where the atomics shall be
> declared `volatile`?

I just said one: loads and stores.

std::atomic<int> a;
a.store(0);
a.store(1);
a.load();
return a.load();

Since this isn't volatile, even if a is a global variable, the compiler is
allowed to perform optimisations. None of them may do so right now, but they
may in the future and further drive Hyman crazy.

IA-64 may even require it, to avoid a write-after-write collision (unless it
adds a stop bit between the two stores).

Tony V E

unread,
Mar 26, 2018, 10:30:56 PM3/26/18
to Standard Proposals
P.S. Linux might (soon or already) use atomics + volatile in the kernel, but only if we can't find something better (like fixing memory_order_consume, etc).

 

IA-64 may even require it, to avoid a write-after-write collision (unless it
adds a stop bit between the two stores).

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
   Software Architect - Intel Open Source Technology Center



--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.



--
Be seeing you,
Tony

Hyman Rosen

unread,
Mar 27, 2018, 1:58:00 AM3/27/18
to std-pr...@isocpp.org
On Mon, Mar 26, 2018 at 8:56 PM, Thiago Macieira <thi...@macieira.org> wrote:
Since this isn't volatile, even if a is a global variable, the compiler is
allowed to perform optimisations. None of them may do so right now, but they
may in the future and further drive Hyman crazy.

Driving *me* crazy isn't really the problem:
<https://randomascii.wordpress.com/2018/01/07/finding-a-cpu-design-bug-in-the-xbox-360/>

Optimizationism turns reliable and predictable behavior into a game of telephone,
where the programmer's instructions get more and more scrambled until the result
is a mess that doesn't do what the programmer wanted.  But it sure does it a lot faster!

inkwizyt...@gmail.com

unread,
Mar 27, 2018, 7:39:37 PM3/27/18
to ISO C++ Standard - Future Proposals

I probably read different article, where is any mention of compiler optimization?
We have only case of bugged instruction that cause bad effect even if not executed directly.

BTW optimization is problem if you try be clever and work against it, if you write code that
expect optimization then you can stop waste time writing manual optimizations and your code will run faster.

Hyman Rosen

unread,
Mar 28, 2018, 12:49:39 AM3/28/18
to std-pr...@isocpp.org
On Tue, Mar 27, 2018 at 7:39 PM, <inkwizyt...@gmail.com> wrote:
I probably read different article, where is any mention of compiler optimization?

You think optimizationism affects (or infects...) only compiler writers?

BTW optimization is problem if you try be clever and work against it

Compiler optimization is a problem when it subverts the clear intent of the
programmer in order to make the wrong code faster.  Whenever a compiler
says "the programmer could not have meant to say this, so I'll ignore it" it
is making a grave error.

Farid Mehrabi

unread,
Apr 2, 2018, 4:13:56 PM4/2/18
to std-proposals
Short answer is: the number and order of reads and writes to a volatile object must be preserved as programmed, in the user thread; if the compiler strictly follows the standards, any read or write from/to the volatile object must accesses the actual memory location. This is different from synchronization mechanisms where normally the atomicity of a read>modify>write sequence is implied - either systemwide or process wide. 
I can hardly imagine any other use case than external hardware communication such as DMA or memory mapped IO. In the embedded world and system/peripheral programming, this usage of the keyword becomes bold.

Regards,
FM.  

در تاریخ ۲۳ مارس ۲۰۱۸ ۱۰:۵۱ قبل‌ازظهر، "Mingxin Wang" <wmx16...@163.com> نوشت:
I am confusing about the necessity of the keyword `volatile` in C++, as we already have the atomics, and this keyword is not able to deal with processor optimizations. Is there any meaningful use case that depends on this keyword in C++ (except for the atomic library in C)? If not, I think it shall be deprecated or removed, like the keyword `register`.

I am looking forward to your comments!

Mingxin Wang

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Hyman Rosen

unread,
Apr 2, 2018, 4:47:53 PM4/2/18
to std-pr...@isocpp.org
On Mon, Apr 2, 2018 at 4:13 PM, Farid Mehrabi <farid....@gmail.com> wrote:
I can hardly imagine any other use case than external hardware communication such as DMA or memory mapped IO.

As I already said earlier in the thread, my main use for it has been, and continues
to be, forcing the compiler to write floating-point expressions to memory in those
cases where I need precisely specified results.  Otherwise the compiler may keep
evaluated expressions in higher precision registers so that they do not have their
exactly specified correct values.

<https://gcc.gnu.org/wiki/FloatingPointMath>
For legacy x86 processors without SSE2 support, and for m68080 processors,
GCC is only able to fully comply with IEEE 754 semantics for the IEEE double
extended (long double) type. Operations on IEEE double precision and IEEE
single precision values are performed using double extended precision.
In order to have these operations rounded correctly, GCC would have to save
the FPU control and status words, enable rounding to 24 or 53 mantissa bits
and then restore the FPU state. This would be far too expensive.

Bengt Gustafsson

unread,
Apr 3, 2018, 5:01:38 AM4/3/18
to ISO C++ Standard - Future Proposals
For the particular use of floating point precision control volatile seems very inefficient as you don't really need writing and reading main memory. Maybe an intrinsic std::preserve_precision() would be a good thing to propose?

Hyman Rosen

unread,
Apr 3, 2018, 9:37:40 AM4/3/18
to std-pr...@isocpp.org
On Tue, Apr 3, 2018 at 5:01 AM, Bengt Gustafsson <bengt.gu...@beamways.com> wrote:
For the particular use of floating point precision control volatile seems very inefficient as you don't really need writing and reading main memory. Maybe an intrinsic std::preserve_precision() would be a good thing to propose?

On the contrary, if the compiler is going to insist on doing the wrong thing
on behalf of the optimizationists, volatile gives me just the control I need
(i.e., on particular results) and does it in a standard-conforming way that's
already in the language, and in C as well, since the problem exists there also.

I believe the language standards already specify that casts to float and double
and assignments to variables are supposed to use the precision of the target.
But gcc refuses to honor that.  It does honor volatile, though.

Andrey Semashev

unread,
Apr 3, 2018, 10:48:59 AM4/3/18
to std-pr...@isocpp.org
On 04/03/18 16:37, Hyman Rosen wrote:
> On Tue, Apr 3, 2018 at 5:01 AM, Bengt Gustafsson
> <bengt.gu...@beamways.com <mailto:bengt.gu...@beamways.com>>
> wrote:
>
> For the particular use of floating point precision control volatile
> seems very inefficient as you don't really need writing and reading
> main memory. Maybe an intrinsic std::preserve_precision() would be a
> good thing to propose?
>
> On the contrary, if the compiler is going to insist on doing the wrong thing
> on behalf of the optimizationists, volatile gives me just the control I need
> (i.e., on particular results) and does it in a standard-conforming way
> that's
> already in the language, and in C as well, since the problem exists
> there also.

AFAICS, C allows to perform operations with increased precision, see C11
6.3.1.8/2. I do not find a similar grant in C++ though, but I consider
it a very useful one, which allows things like FMA.

I'm struggling to see a use case where I would want reduced precision of
math operations where they could potentially be more precise for free.
Your use of volatile for this purpose is certainly unusual and, I'd
wager, not the intended use case for it.

Nicol Bolas

unread,
Apr 3, 2018, 11:00:55 AM4/3/18
to ISO C++ Standard - Future Proposals

There have been cases where I've needed, not "reduced" precision, but consistent precision: computations that give binary-identical results across implementations and even CPUs. Basically, "follow the IEEE-754 standard exactly". You can get that with integer math, but with floats, it's a lot harder.
 

Hyman Rosen

unread,
Apr 3, 2018, 11:33:02 AM4/3/18
to std-pr...@isocpp.org
On Tue, Apr 3, 2018 at 11:00 AM, Nicol Bolas <jmck...@gmail.com> wrote:
AFAICS, C allows to perform operations with increased precision, see C11
6.3.1.8/2. I do not find a similar grant in C++ though, but I consider
it a very useful one, which allows things like FMA.

[expr]/13 in N4700
The values of the floating operands and the results of floating expressions may
be represented in greater precision and range than that required by the type;
the types are not changed thereby.
 

I'm struggling to see a use case where I would want reduced precision of
math operations where they could potentially be more precise for free.

There have been cases where I've needed, not "reduced" precision, but
consistent precision: computations that give binary-identical results across
implementations and even CPUs. Basically, "follow the IEEE-754 standard
exactly". You can get that with integer math, but with floats, it's a lot harder.

Yes.  In my use cases, this usually happens around conversions between
binary and decimal, which are extraordinarily delicate computations.  Having
the compiler inject arbitrary precision is bad.  We had similar issues trying to
convert a fractional number of seconds as a double into an integer pair of
seconds and nanoseconds and to get the same results everywhere.  (On all
of our platforms, doubles use the same IEEE-754 representation, and it is
simply unacceptable to us that a number with an exact value will convert to
different results on different platforms.  We regard that as an error, and using
volatile helped us fix the problem.)

Your use of volatile for this purpose is certainly unusual and, I'd
wager, not the intended use case for it.

I don't care what anyone intended.  It works, and it's portable.

Jeff Hammond

unread,
Apr 3, 2018, 12:41:56 PM4/3/18
to std-pr...@isocpp.org
On Tue, Apr 3, 2018 at 7:48 AM, Andrey Semashev <andrey....@gmail.com> wrote:
On 04/03/18 16:37, Hyman Rosen wrote:

On Tue, Apr 3, 2018 at 5:01 AM, Bengt Gustafsson <bengt.gu...@beamways.com <mailto:bengt.gustafsson@beamways.com>> wrote:

    For the particular use of floating point precision control volatile
    seems very inefficient as you don't really need writing and reading
    main memory. Maybe an intrinsic std::preserve_precision() would be a
    good thing to propose?

On the contrary, if the compiler is going to insist on doing the wrong thing
on behalf of the optimizationists, volatile gives me just the control I need
(i.e., on particular results) and does it in a standard-conforming way that's
already in the language, and in C as well, since the problem exists there also.

AFAICS, C allows to perform operations with increased precision, see C11 6.3.1.8/2. I do not find a similar grant in C++ though, but I consider it a very useful one, which allows things like FMA.

I'm struggling to see a use case where I would want reduced precision of math operations where they could potentially be more precise for free. Your use of volatile for this purpose is certainly unusual and, I'd wager, not the intended use case for it.

Reproducibility and consistency.

For example, if a debug build contains a statement that causes the flushing of the x87 register but which otherwise does not touch non-debug numerical data, it will perturb the numerical results, leading the application developer to bang their head against the wall because the debug build gives different answers than the production build.

For example, if somebody is running a code on x86 and PowerPC platforms, they expect to get the same answer if the code does not depend on the architecture.  If the x86 execution uses x87, it may lead to different results.

The latter example is similar to the case of FMA.  There are application developers who use std::fma on systems that lack hardware support in order to get numerical reproducibility in spite of the cost of implementing the singly-rounded fma in software.

In many cases, these are not academic issues.  Some of the users involved in the aforementioned examples are required to implement numerically reproducible and/or numerically consistent code for regulatory reasons.

Jeff 

--

Andrey Semashev

unread,
Apr 3, 2018, 5:28:54 PM4/3/18
to std-pr...@isocpp.org
On 04/03/18 19:41, Jeff Hammond wrote:
>
>
> On Tue, Apr 3, 2018 at 7:48 AM, Andrey Semashev
> <andrey....@gmail.com <mailto:andrey....@gmail.com>> wrote:
>
> On 04/03/18 16:37, Hyman Rosen wrote:
>
> On Tue, Apr 3, 2018 at 5:01 AM, Bengt Gustafsson
> <bengt.gu...@beamways.com
> <mailto:bengt.gu...@beamways.com>
> <mailto:bengt.gu...@beamways.com
> <mailto:bengt.gu...@beamways.com>>> wrote:
>
>     For the particular use of floating point precision control
> volatile
>     seems very inefficient as you don't really need writing and
> reading
>     main memory. Maybe an intrinsic std::preserve_precision()
> would be a
>     good thing to propose?
>
> On the contrary, if the compiler is going to insist on doing the
> wrong thing
> on behalf of the optimizationists, volatile gives me just the
> control I need
> (i.e., on particular results) and does it in a
> standard-conforming way that's
> already in the language, and in C as well, since the problem
> exists there also.
>
>
> AFAICS, C allows to perform operations with increased precision, see
> C11 6.3.1.8/2 <http://6.3.1.8/2>. I do not find a similar grant in
> C++ though, but I consider it a very useful one, which allows things
> like FMA.
>
> I'm struggling to see a use case where I would want reduced
> precision of math operations where they could potentially be more
> precise for free. Your use of volatile for this purpose is certainly
> unusual and, I'd wager, not the intended use case for it.
>
> Reproducibility and consistency.
>
> For example, if a debug build contains a statement that causes the
> flushing of the x87 register but which otherwise does not touch
> non-debug numerical data, it will perturb the numerical results, leading
> the application developer to bang their head against the wall because
> the debug build gives different answers than the production build.
>
> For example, if somebody is running a code on x86 and PowerPC platforms,
> they expect to get the same answer if the code does not depend on the
> architecture.  If the x86 execution uses x87, it may lead to different
> results.

I think, that expectation is wrong. The language doesn't give you such
guarantees.

> In many cases, these are not academic issues.  Some of the users
> involved in the aforementioned examples are required to implement
> numerically reproducible and/or numerically consistent code for
> regulatory reasons.

Personally, I try to never depend on the exact result when FP is
involved. I consider it my design mistake if such need arises and
rewrite the code to avoid that. This follows from the nature of FP in
C/C++, including that the language does not guarantee that FP is
implemented as IEEE-754 in the first place. So, given this my rule of
thumb, I do not care if the result is more or less precise, in the
reasonable/acceptable error range. More precise is better, though.

I realize there must be scientific applications which are more demanding
wrt. result accuracy, but even there I find it hard to imagine that a
strict binary equivalence of results are needed. More likely, the
acceptable error is less, and if that error is nearing FP precision,
most likely you need to use a specialized library for high precision math.

I'm not sure what kind of regulatory reasons you mean, but if those are
of non-technical nature then, well, tough luck. I guess, using a
specialized library in such case is an appropriate solution as well.

Hyman Rosen

unread,
Apr 3, 2018, 6:05:29 PM4/3/18
to std-pr...@isocpp.org
On Tue, Apr 3, 2018 at 5:28 PM, Andrey Semashev <andrey....@gmail.com> wrote:
I think, that expectation is wrong. The language doesn't give you such guarantees.

I write programs to achieve certain results.  Those results must be achieved.
If the basic constructs of the language can achieve those results, fine.
Otherwise the inability of the language must be circumvented.

Personally, I try to never depend on the exact result when FP is involved.

That's nice.  You don't, however, get to tell me what I need my code to do.

 well, tough luck

Yes, that summarizes the optimizationist viewpoint in a nutshell. 

Ren Industries

unread,
Apr 3, 2018, 6:07:25 PM4/3/18
to std-pr...@isocpp.org
It's not the "optimizationists" fault if you want to do something the language doesn't guarantee. You aren't writing c++ at that point, you are writing to the compiler specifications. 

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Andrey Semashev

unread,
Apr 3, 2018, 6:37:15 PM4/3/18
to std-pr...@isocpp.org
On 04/04/18 01:05, Hyman Rosen wrote:
> On Tue, Apr 3, 2018 at 5:28 PM, Andrey Semashev
> <andrey....@gmail.com <mailto:andrey....@gmail.com>> wrote:
>
> Personally, I try to never depend on the exact result when FP is
> involved.
>
> That's nice.  You don't, however, get to tell me what I need my code to do.

I never did.

Thiago Macieira

unread,
Apr 3, 2018, 8:37:21 PM4/3/18
to std-pr...@isocpp.org
On terça-feira, 3 de abril de 2018 08:00:55 PDT Nicol Bolas wrote:
> > I'm struggling to see a use case where I would want reduced precision of
> > math operations where they could potentially be more precise for free.
>
> There have been cases where I've needed, not "reduced" precision, but
> *consistent* precision: computations that give binary-identical results
> across implementations and even CPUs. Basically, "follow the IEEE-754
> standard exactly". You can get that with integer math, but with floats,
> it's a lot harder.

That's usually the case. The extra precision may be beneficial, except when
you're writing bit-exact unit tests.

I was discussing this with some colleagues who maintain the Mesa library and
they mentioned an upgrade from GCC 6 to 7 broke their unit tests on 32-bit
x86, for the same sources. They tracked down to the extra precision, a problem
they worked around with -mfpmath=sse.

Thiago Macieira

unread,
Apr 3, 2018, 10:01:41 PM4/3/18
to std-pr...@isocpp.org
On terça-feira, 3 de abril de 2018 17:37:16 PDT Thiago Macieira wrote:
> > There have been cases where I've needed, not "reduced" precision, but
> > *consistent* precision: computations that give binary-identical results
> > across implementations and even CPUs. Basically, "follow the IEEE-754
> > standard exactly". You can get that with integer math, but with floats,
> > it's a lot harder.
>
> That's usually the case. The extra precision may be beneficial, except when
> you're writing bit-exact unit tests.

You may also have a problem when an algorithm converges or fails to do so due
to numeric instability. This was more common in the days where 32-bit x86 was
more relevant for development, when algorithms would work with the extended
intermediate precision, but would fail if run on other platforms due to
accumulation of errors.

Bengt Gustafsson

unread,
Apr 4, 2018, 12:50:51 AM4/4/18
to ISO C++ Standard - Future Proposals
@Hyman: My intention was that the library function would be an indication to the compiler that the extended precision has to be truncated. Otherwise it would only be a nop. Implementation could be a few assembly instructions or, better, integrated into the compiler to affect code generation to achieve the desired result. On architectures without extra precision it would be correct for it to be a nop though.

floria...@gmail.com

unread,
Apr 4, 2018, 5:54:59 AM4/4/18
to ISO C++ Standard - Future Proposals
You are using the wrong tool for that purpose.

Every current compiler is able to support the full IEEE754 compliance if told so (even the greatest evil optimizationist compiler ever: icc).
If you want the compiler to generate loads and stores between floating point operations on x87, gcc has -ffloat-store which will do exactly what you want without the overhead of volatile.
But according to gcc, if you don't have SSE, it's near to impossible to have bit correct results for that (double rounding issue): https://gcc.gnu.org/wiki/FloatingPointMath#Note_on_x86_and_m68080_floating-point_math
Please note that even if you use volatile, you can run into the double rounding issue.

But now, we can safely assume your hardware supports SSE, so -mfpmath=sse -msse2 will do the trick (for x86_64, it is the default, and is not needed). And then, you are good: you can write bit accurate floating point code that will give the exact same results on every single architecture (as long as it is IEEE754 compliant).

So no and no, volatile is neither needed for this, nor giving you the right answer.

Hyman Rosen

unread,
Apr 4, 2018, 11:27:25 AM4/4/18
to std-pr...@isocpp.org
On Wed, Apr 4, 2018 at 5:54 AM, <floria...@gmail.com> wrote:
You are using the wrong tool for that purpose.

Every current compiler is able to support the full IEEE754 compliance if told so (even the greatest evil optimizationist compiler ever: icc).
If you want the compiler to generate loads and stores between floating point operations on x87, gcc has -ffloat-store which will do exactly what you want without the overhead of volatile.

In a production environment, it can be significantly more difficult to specialize
compilation flags for specific files than to modify the code not to require such
flags.

So no and no, volatile is neither needed for this, nor giving you the right answer.

And yet, it is working as the language specifies it to work, it works just as we expect it
to, and it gives us the answers we want.  (We have not encountered problems due to
double rounding in our tests, but that may be accidental.  We have tested "difficult"
numbers, such as found here: <http://www.icir.org/vern/papers/testbase-report.pdf>)

Apparently "this is the language specification, you have to accept it" is only good for
the features people like.

inkwizyt...@gmail.com

unread,
Apr 4, 2018, 6:01:42 PM4/4/18
to ISO C++ Standard - Future Proposals

One question, how many `volatile` your code use? IMHO it should be used only in one function:
float std_precision(float f) { volatile float h = f; return h;  }
And this could be replaced by some compiler intrinsic or asm block.
This this will be more portable than trying rally on guaranties that nobody wants to give you.

Hyman Rosen

unread,
Apr 4, 2018, 7:08:08 PM4/4/18
to std-pr...@isocpp.org
On Wed, Apr 4, 2018 at 6:01 PM, <inkwizyt...@gmail.com> wrote:
One question, how many `volatile` your code use? IMHO it should be used only in one function:
float std_precision(float f) { volatile float h = f; return h;  }
And this could be replaced by some compiler intrinsic or asm block.

I just declare variables to be volatile when I need the precision guarantee,
and comment those cases.  They are relatively rare.
 
This this will be more portable than trying rally on guaranties that nobody wants to give you.

The guarantees are given to me by the standards.  I understand that there are
people (optimizationists, I assume) who want to take them away, but they are
still there.

Thiago Macieira

unread,
Apr 4, 2018, 7:30:59 PM4/4/18
to std-pr...@isocpp.org
On Wednesday, 4 April 2018 16:07:45 PDT Hyman Rosen wrote:
> > One question, how many `volatile` your code use? IMHO it should be used
> > only in one function:
> > float std_precision(float f) { volatile float h = f; return h; }
> > And this could be replaced by some compiler intrinsic or asm block.
>
> I just declare variables to be volatile when I need the precision guarantee,
> and comment those cases. They are relatively rare.

You may want to apply the suggestion, if nothing else to centralise an #ifdef
for 387 and m68k math. Otherwise, you're incurring a high latency memory spill
compared to register-only.

> The guarantees are given to me by the standards. I understand that there
> are
> people (optimizationists, I assume) who want to take them away, but they are
> still there.

No one is asking to take this one away. What we're suggesting is that there
may be better ways to do this by eventually shifting the requirement to the
compiler.

Hyman Rosen

unread,
Apr 5, 2018, 1:46:00 AM4/5/18
to std-pr...@isocpp.org
On Wed, Apr 4, 2018 at 7:30 PM, Thiago Macieira <thi...@macieira.org> wrote:
Otherwise, you're incurring a high latency memory spill compared to register-only.

Most of this stuff is in tiny components of huge systems.  I would be astonished if
it was on anyone's critical path.  And we deal with strings, vectors, maps, not to
mention networking, xml and json encoding, and complex financial calculations.
Code is reading and writing memory all the time.  One more variable isn't going
to get super-optimized treatment unless it's a proven bottleneck.

When conversion to decimal floating-point actually was a bottleneck, we came
up with the proverbial better algorithm that improved performance for a restricted
range of values that happened to cover most of the important use cases.

No one is asking to take this one away. What we're suggesting is that there
may be better ways to do this by eventually shifting the requirement to the
compiler.

The compiler already has this requirement.  It has chosen to ignore it.  According
to the standard, casting to a floating-point type or assigning to a floating-point
variable must do the downscaling.  Gcc does not to do that.

floria...@gmail.com

unread,
Apr 5, 2018, 5:41:03 AM4/5/18
to ISO C++ Standard - Future Proposals

The compiler already has this requirement.  It has chosen to ignore it.  According
to the standard, casting to a floating-point type or assigning to a floating-point
variable must do the downscaling.  Gcc does not to do that.

Actually, GCC does... for non-x87 targets...
The most frustrating and simple I have (requires C++11 or newer) is:
float square(float x) {
 
return std::pow(x, 2);
}

This is fully optimized by GCC, but the output is: converts x to double, multiply it by itself, and converts back to float.
So here, in this case, GCC does respect the requirements (int cannot be promoted to float, but can be promoted to double, so the standard requires here that both get promoted to double).
And if you use this inside a big chain of computation using only floats, I can assure you that the double conversion is kept.

I think your main problem is: x87 is not maintained anymore. (and also: x87 is crap compared to what we have now, but this has nothing to do with the compiler)
That's why your best bet here would be to use SSE2 when targeting x86 32bits. Doing that, you will have full IEEE754 compliance you need (and will most likely be faster). And I really doubt you still need to target machines with x87 and without SSE2.

Just to clarify, I'm not saying we should remove volatile, but just that you have a volatile-based hack that appears to work, while there are proper solutions to your problem (in that case).

Dilip Ranganathan

unread,
Apr 5, 2018, 10:19:23 AM4/5/18
to std-pr...@isocpp.org
On Thu, Apr 5, 2018 at 5:41 AM, <floria...@gmail.com> wrote:

The compiler already has this requirement.  It has chosen to ignore it.  According
to the standard, casting to a floating-point type or assigning to a floating-point
variable must do the downscaling.  Gcc does not to do that.

Actually, GCC does... for non-x87 targets...

This is the source of this entire discussion, I guess?
https://gcc.gnu.org/wiki/x87note
 

floria...@gmail.com

unread,
Apr 5, 2018, 10:57:36 AM4/5/18
to ISO C++ Standard - Future Proposals

This is the source of this entire discussion, I guess?
https://gcc.gnu.org/wiki/x87note
 
Well, yes...

My point is: Hyman's problem is (apparently?) only for x87 targets.
However, GCC devs don't maintain x87 anymore (this part of the documentation is very old, and contradicts itself here and there).
So everything he said is basically right, but only for an old, deprecated (and buggy) architecture.
But for other still maintained architectures, nothing he said holds.
Currently, compilers do respect floating constraints (by default on GCC, with compiler flags on ICC).

While I'm pretty it is still relatively easy to find places where old x86 (32 bits) machines not supporting SSE4 are needed, I doubt those are old enough to not support SSE2.
Thus, I think the proper fix is to compile with SSE support, not (ab)using volatile.

Hyman Rosen

unread,
Apr 5, 2018, 11:50:52 AM4/5/18
to std-pr...@isocpp.org
On Thu, Apr 5, 2018 at 5:41 AM, <floria...@gmail.com> wrote:
The compiler already has this requirement.  It has chosen to ignore it.  According
to the standard, casting to a floating-point type or assigning to a floating-point
variable must do the downscaling.  Gcc does not to do that.

Actually, GCC does... for non-x87 targets...

We have legacy systems and legacy compilers, and we still build for x87.
The errors I have dealt with were reported from the field.  And as I said, it
is easy for me to make changes in the code, but much more difficult to make
changes in the build environment.

The most frustrating and simple I have (requires C++11 or newer) is:
float square(float x) {
 
return std::pow(x, 2);
}

This is fully optimized by GCC, but the output is: converts x to double, multiply it by itself, and converts back to float.
So here, in this case, GCC does respect the requirements (int cannot be promoted to float, but can be promoted to double, so the standard requires here that both get promoted to double).
And if you use this inside a big chain of computation using only floats, I can assure you that the double conversion is kept.

I don't understand how this applies to my problem.  But I adapted your code:

#include <cmath>
#include <cstdio>
#include <iostream>
#include <iomanip>

using std::pow;

void a(const char *a, const char *b)
{
    float f;
    int   n;
    sscanf(a, "%f", &f);
    sscanf(b, "%d", &n);
    float g = f;
    float p = std::pow(f, n);
    g += p;
    std::cout << std::setprecision(17) << g << "\n";
}

void b(const char *a, const char *b)
{
    float f;
    int   n;
    sscanf(a, "%f", &f);
    sscanf(b, "%d", &n);
    volatile float g = f;
    volatile float p = std::pow(f, n);
    g += p;
    std::cout << std::setprecision(17) << g << "\n";
}

int main(int c, char **v)
{
    if (c > 2) {
        a(v[1], v[2]);
        b(v[1], v[2]);
    }
}

I compiled it with a version 7.3 g++, using g++ -std=c++17 -mfpmath=387 -O1.
When run with the command line arguments
.1 and 2, it produces
0.11000000219792128
0.10999999940395355

I think your main problem is: x87 is not maintained anymore.

My main problem, as illustrated by the code above, is that g++ disobeys the
standard when doing floating-point arithmetic.

That's why your best bet here would be to use SSE2 when targeting x86 32bits. Doing that, you will have full IEEE754 compliance you need (and will most likely be faster). And I really doubt you still need to target machines with x87 and without SSE2.

I do not control my company's build environment, and we do build in x87 mode.
Perhaps at some point we won't, but that day hasn't arrived yet.

Just to clarify, I'm not saying we should remove volatile, but just that you have
a volatile-based hack that appears to work, while there are proper solutions to
your problem (in that case).

Notice how optimizationism poisons everything.  Not only does the standard make
poor choices that privilege potential efficiency over code clarity and consistency, but
even when the standard makes the correct choice, that choice is ignored in order to
again privilege that potential efficiency.

Hyman Rosen

unread,
Apr 5, 2018, 12:01:19 PM4/5/18
to std-pr...@isocpp.org
On Thu, Apr 5, 2018 at 10:57 AM, <floria...@gmail.com> wrote:
So everything he said is basically right, but only for an old, deprecated (and buggy) architecture.

You compute on the architecture you have, not on the architecture you want.
There isn't anything about the x87 architecture that precludes g++ from doing
the right thing, given that using volatile in fact does the right thing.  The authors
of g++ deliberately chose to do the wrong thing for the sake of optimizationism;
they preferred to produce the incorrect, non-standard-complying result because
they could do that faster than producing the correct, standard-complying result.

floria...@gmail.com

unread,
Apr 5, 2018, 12:27:39 PM4/5/18
to ISO C++ Standard - Future Proposals


The most frustrating and simple I have (requires C++11 or newer) is:
float square(float x) {
 
return std::pow(x, 2);
}

This is fully optimized by GCC, but the output is: converts x to double, multiply it by itself, and converts back to float.
So here, in this case, GCC does respect the requirements (int cannot be promoted to float, but can be promoted to double, so the standard requires here that both get promoted to double).
And if you use this inside a big chain of computation using only floats, I can assure you that the double conversion is kept.

I don't understand how this applies to my problem.

My point here is, if you do compile with SSE support this square function, GCC does keep the double conversion, and so respects IEEE754 despite optimizationism:

float square(float x) {
 
return std::pow(x, 2);
}
gives (for x86_64):
square(float):
  cvtss2sd
%xmm0, %xmm0
  mulsd
%xmm0, %xmm0
  cvtsd2ss
%xmm0, %xmm0
  ret

As you can see: the multiplication is done in double precision, exactly as it should be in that very case, while a compiler that would not comply with IEEE754 would do the multiplication in single precision.

In the same vein, but probably easier:
float dotprod(const float* A, int n) {
 
float s = 0.f;
 
for (int i = 0; i < n; i++) {
    s
+= A[i];
 
}
 
return s;
}
This is not vectorizable by GCC (unless you use -ffast-math) because this would change the result.


So, GCC has the correct behavior.
But as x87 is not maintained anymore, you are using very old GCC code, and this code was not respecting this.
They have made a choice long ago that was exactly what you describe. (Could they do better at that point? I'm not even sure)

But what I say is: they change their mind and corrected this on newer code, but as you use deprecated architecture, you use part of GCC that was not fixed (and will probably never be fixed).


I think your main problem is: x87 is not maintained anymore.

My main problem, as illustrated by the code above, is that g++ disobeys the
standard when doing floating-point arithmetic.

Because you are targeting an architecture that is not maintained anymore. All the others are perfectly fine...
BTW, you use a buggy architecture: it is normal to encounter bugs at that point (and yes, x87 is buggy on its own).


Notice how optimizationism poisons everything.  Not only does the standard make
poor choices that privilege potential efficiency over code clarity and consistency, but
even when the standard makes the correct choice, that choice is ignored in order to
again privilege that potential efficiency.

If you do it right, the compiler also does it right.
In the past, things were different. But now, compilers (especially GCC) are really good at respecting both your code and the standard.
But if your intent is different from your code, don't be surprise that the compiler does not give what you expect.

You compute on the architecture you have, not on the architecture you want.
There isn't anything about the x87 architecture that precludes g++ from doing
the right thing, given that using volatile in fact does the right thing.  The authors
of g++ deliberately chose to do the wrong thing for the sake of optimizationism;
they preferred to produce the incorrect, non-standard-complying result because
they could do that faster than producing the correct, standard-complying result.

Do you really still have Pentium 3 and older machines?
If GCC does not maintain x87 anymore (and thus is buggy here), it is simple: this architecture is buggy, and not use for quite a long time now.

Jens Maurer

unread,
Apr 5, 2018, 1:55:50 PM4/5/18
to std-pr...@isocpp.org
On 04/05/2018 07:45 AM, Hyman Rosen wrote:
> The compiler already has this requirement. It has chosen to ignore it. According
> to the standard, casting to a floating-point type or assigning to a floating-point
> variable must do the downscaling. Gcc does not to do that.

We have very little requirements on floating-point arithmetic in the C++ standard.

[basic.fundamental] p8 says (for example):

"[ Note: This document imposes no requirements on the accuracy of floating-point
operations; see also 21.3. -- end note ]"

What normative statement in the standard makes you believe that
"casting to a floating-point type [...] must do the downscaling"?

Jens


Thiago Macieira

unread,
Apr 5, 2018, 3:25:03 PM4/5/18
to std-pr...@isocpp.org
On Thursday, 5 April 2018 07:57:36 PDT floria...@gmail.com wrote:
> While I'm pretty it is still relatively easy to find places where old x86
> (32 bits) machines not supporting SSE4 are needed, I doubt those are old
> enough to not support SSE2.

Intel has produced recent CPUs without SSE2. But they also lack 387 FPU too,
so you need software FP emulation anyway.

It's the Quark line of microcontrollers.

Hyman Rosen

unread,
Apr 5, 2018, 3:34:29 PM4/5/18
to std-pr...@isocpp.org
On Thu, Apr 5, 2018 at 1:55 PM, Jens Maurer <Jens....@gmx.net> wrote:
What normative statement in the standard makes you believe that
"casting to a floating-point type [...] must do the downscaling"?

In N4700, [expr]/13 says

The values of the floating operands and the results of floating expressions
may be represented in greater 
precision and range than that required by
the type; the types are not changed thereby.64

The associated footnote says
64) The cast and assignment operators must still perform their specific
conversions as described in 8.4, 8.2.9 and 8.18.

Thiago Macieira

unread,
Apr 5, 2018, 7:24:29 PM4/5/18
to std-pr...@isocpp.org
On Thursday, 5 April 2018 12:34:05 PDT Hyman Rosen wrote:
> On Thu, Apr 5, 2018 at 1:55 PM, Jens Maurer <Jens....@gmx.net> wrote:
> > What normative statement in the standard makes you believe that
> > "casting to a floating-point type [...] must do the downscaling"?
>
> In N4700, [expr]/13 says
>
> *The values of the floating operands and the results of floating
> expressionsmay be represented in greater *
> *precision and range than that required bythe type; the types are not
> changed thereby.64*
>
> The associated footnote says
>
>
> *64) The cast and assignment operators must still perform their
> specificconversions as described in 8.4, 8.2.9 and 8.18.*

In other words:

a = b * c + d;

is allowed to use extra precision in intermediary results, like using an FMA
hardware instruction. But

a = b * c;
a += d;

is not, because there was an assignment to a and the footnote says the
assignment must perform the conversion.
Reply all
Reply to author
Forward
0 new messages