I am reading through the new C++ FAQ and I see that even if x == y
for double x, y;
then it is possible for:
std::cos(x) == std::cos(y)
to evaluate to false
. This is because the machine can have a processor which supports extended precision, such that one part of the ==
is a 64 bit number while the other one is an 80 bit number.
However, the next example seems to be incorrect:
void foo(double x, double y)
{
double cos_x = cos(x);
double cos_y = cos(y);
// the behavior might depend on what's in here
if (cos_x != cos_y) {
std::cout << "Huh?!?\n"; // You might end up here when x == y!!
}
}
As far as I read on en.cppreference.com here:
Cast and assignment strip away any extraneous range and precision: this models the action of storing a value from an extended-precision FPU register into a standard-sized memory location.
Hence, assigning:
double cos_x = cos(x);
double cos_y = cos(y);
should trim away any extra precision and make the program perfectly predictable.
So, who is right? The C++ FAQ, or the en.cppreference.com?
A key difference between twin roll casting and DC casting is the solidification rate. While in DC casting it is limited to 1–50 °C/s, it reaches 500–1000 °C/s in a twin roll caster, leading to greatly refined structures with small and finely distributed intermetallic phases, extended solid solutions and fine grain sizes.
Direct Strip Cast Alloys There have been substantial developments in the mass production of iron alloys by direct strip casting (DSC) which produces as-cast strip products of a thickness less than 2 mm. Iron alloys currently produced by DSC include carbon and alloy steels, stainless steel, iron–silicon alloys, and cast iron.
The basic idea of thin slab casting is to cast typically 40 to 80 mm thick slab. The casting rate should be relatively high 4–20 m/min in order to attain the same production rate as with the conventional caster. With conventional slab caster, the withdrawal rate varies from about 1.0 to 1.8 m/min.
A strip casting scrap can also be used as a filler wire. The casting filler wire can be used for casting welding and salvaging casting. But the casting scrap and casting wire must be selected carefully to ensure that there are no slag inclusion and rarefaction defects in them.
Neither the ISOCPP FAQ or cppreference are authoritative sources. cppreference is essentially the equivalent of wikipedia: it contains good information, but anybody can add anything without sources you have to read it with a grain of salt. Are the following three statements true:
Did you catch that? Your particular installation might store the result of one of the cos() calls out into RAM, truncating it in the process, then later compare that truncated value with the untruncated result of the second cos() call. Depending on lots of details, those two values might not be equal.
This is because the machine can have a processor which supports extended precision, such that one part of the
==
is a 64 bit number while the other one is an 80 bit number.Cast and assignment strip away any extraneous range and precision: this models the action of storing a value from an extended-precision FPU register into a standard-sized memory location.
Maybe. It depends on the platform, your compiler, the compiler options, or any number of things. Realistically, the above statements only hold true for GCC in 32-bit mode, which defaults to the legacy x87 FPU (where everything is 80-bits internally) and rounded down to 64-bit when storing in memory. 64-bit uses SSE so double temporaries are always 64-bit. You can force SSE with mfpmath=sse
and -msse2
. Either way, the assembly might look something like this:
movsd QWORD PTR [rsp+8], xmm1
call cos
movsd xmm1, QWORD PTR [rsp+8]
movsd QWORD PTR [rsp], xmm0
movapd xmm0, xmm1
call cos
movsd xmm2, QWORD PTR [rsp]
ucomisd xmm2, xmm0
As you can see there is no truncation of any sort nor 80-bit registers used here.
I wish that assignment or casting would toss away the extra precision. I have worked with "clever" compilers for which this does not happen. Many do, but if you want truly machine-independent code, you need do extra work.
You can force the compiler to always use the value in memory with the expected precision by declaring the variable with the "volatile" keyword.
Some compilers pass parameters in registers rather than on the stack. For these compilers, it could be that x or y could have unintended extra precision. To be totally safe, you could do
volatile double db_x = x, db_y = y;
volatile double cos_x = cos(db_x), cos_y = cos(db_y);
if (cos_x != cos_y)...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With