Search code examples
floating-pointcpu-architecturefloating-accuracy

Equality of floating point numbers after storing/loading/moving


A coworker and I have a disagreement about what can happen when comparing two floating-point numbers that have not been mathematically operated on. i.e. the numbers may have been moved around memory and/or cpu registers, but no math has been done on them. Maybe they have been put in a list and then removed or other various operations.

My experience has led me to believe that doing non-arithmetic operations on floating-point numbers should never change them or be subject to the same rounding errors as arithmetic operations. My coworker contents that the floating-point processing part of the cpu for some modern architectures is allowed to slightly corrupt the number such that equality checks fail, even when only storing/loading/moving the value.

For example, consider this C code:

float* a = (float*)malloc(sizeof(float));
float* b = (float*)malloc(sizeof(float));
*a = 1.0;
*b = 1.0;
int equal = *a == *b;

Are there any circumstances where equal would not be 1?


Solution

  • What you wrote is equivalent to

    float a = 1.0;
    float b = 1.0;
    int equal = a == b;
    

    (using pointers do not change anything as far as the C standard is concerned). So, for the variable a, 1.0 is interpreted in some evaluation format F (depending on FLT_EVAL_METHOD, see ISO C17 5.2.4.2.2p9), then converted to float and stored in a. Ditto for b. As a general rule, storing/reading values must not change them, unless explicitly stated (for instance, the ISO C17 standard explicitly says in 6.2.6.2p3 that on implementations that support negative integer zeros, a negative zero may become a normal zero when stored).

    To answer the question, first consider the conversion of the 1.0 constant (as a string) to F on both lines. ISO C17 says in 6.4.4.2p5: "All floating constants of the same source form shall convert to the same internal format with the same value." So you'll get the same value (in the evaluation format F) in both cases. But note that if you had 1.0 and 1.00 respectively, you might get different values (unlikely, in particular because 1.0 is exactly representable and simple enough, but not forbidden by the C standard).

    Then consider the conversion of the obtained value to float. ISO C17 6.3.1.5p1 says: "When a value of real floating type is converted to a real floating type, if the value being converted can be represented exactly in the new type, it is unchanged." This is the case of the value 1 if 1.0 (used in the example) was converted to 1, so that equal will be 1 in this case. But if 1.0 was converted to some other value, not representable in a float, I think that equal could be 0 (the C standard does not require that conversion of some value to some type always yield the same result, and note that in particular, this is not the case when the rounding mode changes).